1*4882a593Smuzhiyun# 2*4882a593Smuzhiyun# BitBake Toaster Implementation 3*4882a593Smuzhiyun# 4*4882a593Smuzhiyun# Copyright (C) 2014 Intel Corporation 5*4882a593Smuzhiyun# 6*4882a593Smuzhiyun# SPDX-License-Identifier: GPL-2.0-only 7*4882a593Smuzhiyun# 8*4882a593Smuzhiyun 9*4882a593Smuzhiyunimport os 10*4882a593Smuzhiyunimport re 11*4882a593Smuzhiyunimport shutil 12*4882a593Smuzhiyunimport time 13*4882a593Smuzhiyunfrom bldcontrol.models import BuildEnvironment, BuildRequest, Build 14*4882a593Smuzhiyunfrom orm.models import CustomImageRecipe, Layer, Layer_Version, Project, ToasterSetting 15*4882a593Smuzhiyunfrom orm.models import signal_runbuilds 16*4882a593Smuzhiyunimport subprocess 17*4882a593Smuzhiyun 18*4882a593Smuzhiyunfrom toastermain import settings 19*4882a593Smuzhiyun 20*4882a593Smuzhiyunfrom bldcontrol.bbcontroller import BuildEnvironmentController, ShellCmdException, BuildSetupException 21*4882a593Smuzhiyun 22*4882a593Smuzhiyunimport logging 23*4882a593Smuzhiyunlogger = logging.getLogger("toaster") 24*4882a593Smuzhiyun 25*4882a593Smuzhiyuninstall_dir = os.environ.get('TOASTER_DIR') 26*4882a593Smuzhiyun 27*4882a593Smuzhiyunfrom pprint import pformat 28*4882a593Smuzhiyun 29*4882a593Smuzhiyunclass LocalhostBEController(BuildEnvironmentController): 30*4882a593Smuzhiyun """ Implementation of the BuildEnvironmentController for the localhost; 31*4882a593Smuzhiyun this controller manages the default build directory, 32*4882a593Smuzhiyun the server setup and system start and stop for the localhost-type build environment 33*4882a593Smuzhiyun 34*4882a593Smuzhiyun """ 35*4882a593Smuzhiyun 36*4882a593Smuzhiyun def __init__(self, be): 37*4882a593Smuzhiyun super(LocalhostBEController, self).__init__(be) 38*4882a593Smuzhiyun self.pokydirname = None 39*4882a593Smuzhiyun self.islayerset = False 40*4882a593Smuzhiyun 41*4882a593Smuzhiyun def _shellcmd(self, command, cwd=None, nowait=False,env=None): 42*4882a593Smuzhiyun if cwd is None: 43*4882a593Smuzhiyun cwd = self.be.sourcedir 44*4882a593Smuzhiyun if env is None: 45*4882a593Smuzhiyun env=os.environ.copy() 46*4882a593Smuzhiyun 47*4882a593Smuzhiyun logger.debug("lbc_shellcmd: (%s) %s" % (cwd, command)) 48*4882a593Smuzhiyun p = subprocess.Popen(command, cwd = cwd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=env) 49*4882a593Smuzhiyun if nowait: 50*4882a593Smuzhiyun return 51*4882a593Smuzhiyun (out,err) = p.communicate() 52*4882a593Smuzhiyun p.wait() 53*4882a593Smuzhiyun if p.returncode: 54*4882a593Smuzhiyun if len(err) == 0: 55*4882a593Smuzhiyun err = "command: %s \n%s" % (command, out) 56*4882a593Smuzhiyun else: 57*4882a593Smuzhiyun err = "command: %s \n%s" % (command, err) 58*4882a593Smuzhiyun logger.warning("localhostbecontroller: shellcmd error %s" % err) 59*4882a593Smuzhiyun raise ShellCmdException(err) 60*4882a593Smuzhiyun else: 61*4882a593Smuzhiyun logger.debug("localhostbecontroller: shellcmd success") 62*4882a593Smuzhiyun return out.decode('utf-8') 63*4882a593Smuzhiyun 64*4882a593Smuzhiyun def getGitCloneDirectory(self, url, branch): 65*4882a593Smuzhiyun """Construct unique clone directory name out of url and branch.""" 66*4882a593Smuzhiyun if branch != "HEAD": 67*4882a593Smuzhiyun return "_toaster_clones/_%s_%s" % (re.sub('[:/@+%]', '_', url), branch) 68*4882a593Smuzhiyun 69*4882a593Smuzhiyun # word of attention; this is a localhost-specific issue; only on the localhost we expect to have "HEAD" releases 70*4882a593Smuzhiyun # which _ALWAYS_ means the current poky checkout 71*4882a593Smuzhiyun from os.path import dirname as DN 72*4882a593Smuzhiyun local_checkout_path = DN(DN(DN(DN(DN(os.path.abspath(__file__)))))) 73*4882a593Smuzhiyun #logger.debug("localhostbecontroller: using HEAD checkout in %s" % local_checkout_path) 74*4882a593Smuzhiyun return local_checkout_path 75*4882a593Smuzhiyun 76*4882a593Smuzhiyun def setCloneStatus(self,bitbake,status,total,current,repo_name): 77*4882a593Smuzhiyun bitbake.req.build.repos_cloned=current 78*4882a593Smuzhiyun bitbake.req.build.repos_to_clone=total 79*4882a593Smuzhiyun bitbake.req.build.progress_item=repo_name 80*4882a593Smuzhiyun bitbake.req.build.save() 81*4882a593Smuzhiyun 82*4882a593Smuzhiyun def setLayers(self, bitbake, layers, targets): 83*4882a593Smuzhiyun """ a word of attention: by convention, the first layer for any build will be poky! """ 84*4882a593Smuzhiyun 85*4882a593Smuzhiyun assert self.be.sourcedir is not None 86*4882a593Smuzhiyun 87*4882a593Smuzhiyun layerlist = [] 88*4882a593Smuzhiyun nongitlayerlist = [] 89*4882a593Smuzhiyun layer_index = 0 90*4882a593Smuzhiyun git_env = os.environ.copy() 91*4882a593Smuzhiyun # (note: add custom environment settings here) 92*4882a593Smuzhiyun 93*4882a593Smuzhiyun # set layers in the layersource 94*4882a593Smuzhiyun 95*4882a593Smuzhiyun # 1. get a list of repos with branches, and map dirpaths for each layer 96*4882a593Smuzhiyun gitrepos = {} 97*4882a593Smuzhiyun 98*4882a593Smuzhiyun # if we're using a remotely fetched version of bitbake add its git 99*4882a593Smuzhiyun # details to the list of repos to clone 100*4882a593Smuzhiyun if bitbake.giturl and bitbake.commit: 101*4882a593Smuzhiyun gitrepos[(bitbake.giturl, bitbake.commit)] = [] 102*4882a593Smuzhiyun gitrepos[(bitbake.giturl, bitbake.commit)].append( 103*4882a593Smuzhiyun ("bitbake", bitbake.dirpath, 0)) 104*4882a593Smuzhiyun 105*4882a593Smuzhiyun for layer in layers: 106*4882a593Smuzhiyun # We don't need to git clone the layer for the CustomImageRecipe 107*4882a593Smuzhiyun # as it's generated by us layer on if needed 108*4882a593Smuzhiyun if CustomImageRecipe.LAYER_NAME in layer.name: 109*4882a593Smuzhiyun continue 110*4882a593Smuzhiyun 111*4882a593Smuzhiyun # If we have local layers then we don't need clone them 112*4882a593Smuzhiyun # For local layers giturl will be empty 113*4882a593Smuzhiyun if not layer.giturl: 114*4882a593Smuzhiyun nongitlayerlist.append( "%03d:%s" % (layer_index,layer.local_source_dir) ) 115*4882a593Smuzhiyun continue 116*4882a593Smuzhiyun 117*4882a593Smuzhiyun if not (layer.giturl, layer.commit) in gitrepos: 118*4882a593Smuzhiyun gitrepos[(layer.giturl, layer.commit)] = [] 119*4882a593Smuzhiyun gitrepos[(layer.giturl, layer.commit)].append( (layer.name,layer.dirpath,layer_index) ) 120*4882a593Smuzhiyun layer_index += 1 121*4882a593Smuzhiyun 122*4882a593Smuzhiyun 123*4882a593Smuzhiyun logger.debug("localhostbecontroller, our git repos are %s" % pformat(gitrepos)) 124*4882a593Smuzhiyun 125*4882a593Smuzhiyun 126*4882a593Smuzhiyun # 2. Note for future use if the current source directory is a 127*4882a593Smuzhiyun # checked-out git repos that could match a layer's vcs_url and therefore 128*4882a593Smuzhiyun # be used to speed up cloning (rather than fetching it again). 129*4882a593Smuzhiyun 130*4882a593Smuzhiyun cached_layers = {} 131*4882a593Smuzhiyun 132*4882a593Smuzhiyun try: 133*4882a593Smuzhiyun for remotes in self._shellcmd("git remote -v", self.be.sourcedir,env=git_env).split("\n"): 134*4882a593Smuzhiyun try: 135*4882a593Smuzhiyun remote = remotes.split("\t")[1].split(" ")[0] 136*4882a593Smuzhiyun if remote not in cached_layers: 137*4882a593Smuzhiyun cached_layers[remote] = self.be.sourcedir 138*4882a593Smuzhiyun except IndexError: 139*4882a593Smuzhiyun pass 140*4882a593Smuzhiyun except ShellCmdException: 141*4882a593Smuzhiyun # ignore any errors in collecting git remotes this is an optional 142*4882a593Smuzhiyun # step 143*4882a593Smuzhiyun pass 144*4882a593Smuzhiyun 145*4882a593Smuzhiyun logger.info("Using pre-checked out source for layer %s", cached_layers) 146*4882a593Smuzhiyun 147*4882a593Smuzhiyun # 3. checkout the repositories 148*4882a593Smuzhiyun clone_count=0 149*4882a593Smuzhiyun clone_total=len(gitrepos.keys()) 150*4882a593Smuzhiyun self.setCloneStatus(bitbake,'Started',clone_total,clone_count,'') 151*4882a593Smuzhiyun for giturl, commit in gitrepos.keys(): 152*4882a593Smuzhiyun self.setCloneStatus(bitbake,'progress',clone_total,clone_count,gitrepos[(giturl, commit)][0][0]) 153*4882a593Smuzhiyun clone_count += 1 154*4882a593Smuzhiyun 155*4882a593Smuzhiyun localdirname = os.path.join(self.be.sourcedir, self.getGitCloneDirectory(giturl, commit)) 156*4882a593Smuzhiyun logger.debug("localhostbecontroller: giturl %s:%s checking out in current directory %s" % (giturl, commit, localdirname)) 157*4882a593Smuzhiyun 158*4882a593Smuzhiyun # see if our directory is a git repository 159*4882a593Smuzhiyun if os.path.exists(localdirname): 160*4882a593Smuzhiyun try: 161*4882a593Smuzhiyun localremotes = self._shellcmd("git remote -v", 162*4882a593Smuzhiyun localdirname,env=git_env) 163*4882a593Smuzhiyun # NOTE: this nice-to-have check breaks when using git remaping to get past firewall 164*4882a593Smuzhiyun # Re-enable later with .gitconfig remapping checks 165*4882a593Smuzhiyun #if not giturl in localremotes and commit != 'HEAD': 166*4882a593Smuzhiyun # raise BuildSetupException("Existing git repository at %s, but with different remotes ('%s', expected '%s'). Toaster will not continue out of fear of damaging something." % (localdirname, ", ".join(localremotes.split("\n")), giturl)) 167*4882a593Smuzhiyun pass 168*4882a593Smuzhiyun except ShellCmdException: 169*4882a593Smuzhiyun # our localdirname might not be a git repository 170*4882a593Smuzhiyun #- that's fine 171*4882a593Smuzhiyun pass 172*4882a593Smuzhiyun else: 173*4882a593Smuzhiyun if giturl in cached_layers: 174*4882a593Smuzhiyun logger.debug("localhostbecontroller git-copying %s to %s" % (cached_layers[giturl], localdirname)) 175*4882a593Smuzhiyun self._shellcmd("git clone \"%s\" \"%s\"" % (cached_layers[giturl], localdirname),env=git_env) 176*4882a593Smuzhiyun self._shellcmd("git remote remove origin", localdirname,env=git_env) 177*4882a593Smuzhiyun self._shellcmd("git remote add origin \"%s\"" % giturl, localdirname,env=git_env) 178*4882a593Smuzhiyun else: 179*4882a593Smuzhiyun logger.debug("localhostbecontroller: cloning %s in %s" % (giturl, localdirname)) 180*4882a593Smuzhiyun self._shellcmd('git clone "%s" "%s"' % (giturl, localdirname),env=git_env) 181*4882a593Smuzhiyun 182*4882a593Smuzhiyun # branch magic name "HEAD" will inhibit checkout 183*4882a593Smuzhiyun if commit != "HEAD": 184*4882a593Smuzhiyun logger.debug("localhostbecontroller: checking out commit %s to %s " % (commit, localdirname)) 185*4882a593Smuzhiyun ref = commit if re.match('^[a-fA-F0-9]+$', commit) else 'origin/%s' % commit 186*4882a593Smuzhiyun self._shellcmd('git fetch && git reset --hard "%s"' % ref, localdirname,env=git_env) 187*4882a593Smuzhiyun 188*4882a593Smuzhiyun # take the localdirname as poky dir if we can find the oe-init-build-env 189*4882a593Smuzhiyun if self.pokydirname is None and os.path.exists(os.path.join(localdirname, "oe-init-build-env")): 190*4882a593Smuzhiyun logger.debug("localhostbecontroller: selected poky dir name %s" % localdirname) 191*4882a593Smuzhiyun self.pokydirname = localdirname 192*4882a593Smuzhiyun 193*4882a593Smuzhiyun # make sure we have a working bitbake 194*4882a593Smuzhiyun if not os.path.exists(os.path.join(self.pokydirname, 'bitbake')): 195*4882a593Smuzhiyun logger.debug("localhostbecontroller: checking bitbake into the poky dirname %s " % self.pokydirname) 196*4882a593Smuzhiyun self._shellcmd("git clone -b \"%s\" \"%s\" \"%s\" " % (bitbake.commit, bitbake.giturl, os.path.join(self.pokydirname, 'bitbake')),env=git_env) 197*4882a593Smuzhiyun 198*4882a593Smuzhiyun # verify our repositories 199*4882a593Smuzhiyun for name, dirpath, index in gitrepos[(giturl, commit)]: 200*4882a593Smuzhiyun localdirpath = os.path.join(localdirname, dirpath) 201*4882a593Smuzhiyun logger.debug("localhostbecontroller: localdirpath expects '%s'" % localdirpath) 202*4882a593Smuzhiyun if not os.path.exists(localdirpath): 203*4882a593Smuzhiyun raise BuildSetupException("Cannot find layer git path '%s' in checked out repository '%s:%s'. Exiting." % (localdirpath, giturl, commit)) 204*4882a593Smuzhiyun 205*4882a593Smuzhiyun if name != "bitbake": 206*4882a593Smuzhiyun layerlist.append("%03d:%s" % (index,localdirpath.rstrip("/"))) 207*4882a593Smuzhiyun 208*4882a593Smuzhiyun self.setCloneStatus(bitbake,'complete',clone_total,clone_count,'') 209*4882a593Smuzhiyun logger.debug("localhostbecontroller: current layer list %s " % pformat(layerlist)) 210*4882a593Smuzhiyun 211*4882a593Smuzhiyun # Resolve self.pokydirname if not resolved yet, consider the scenario 212*4882a593Smuzhiyun # where all layers are local, that's the else clause 213*4882a593Smuzhiyun if self.pokydirname is None: 214*4882a593Smuzhiyun if os.path.exists(os.path.join(self.be.sourcedir, "oe-init-build-env")): 215*4882a593Smuzhiyun logger.debug("localhostbecontroller: selected poky dir name %s" % self.be.sourcedir) 216*4882a593Smuzhiyun self.pokydirname = self.be.sourcedir 217*4882a593Smuzhiyun else: 218*4882a593Smuzhiyun # Alternatively, scan local layers for relative "oe-init-build-env" location 219*4882a593Smuzhiyun for layer in layers: 220*4882a593Smuzhiyun if os.path.exists(os.path.join(layer.layer_version.layer.local_source_dir,"..","oe-init-build-env")): 221*4882a593Smuzhiyun logger.debug("localhostbecontroller, setting pokydirname to %s" % (layer.layer_version.layer.local_source_dir)) 222*4882a593Smuzhiyun self.pokydirname = os.path.join(layer.layer_version.layer.local_source_dir,"..") 223*4882a593Smuzhiyun break 224*4882a593Smuzhiyun else: 225*4882a593Smuzhiyun logger.error("pokydirname is not set, you will run into trouble!") 226*4882a593Smuzhiyun 227*4882a593Smuzhiyun # 5. create custom layer and add custom recipes to it 228*4882a593Smuzhiyun for target in targets: 229*4882a593Smuzhiyun try: 230*4882a593Smuzhiyun customrecipe = CustomImageRecipe.objects.get( 231*4882a593Smuzhiyun name=target.target, 232*4882a593Smuzhiyun project=bitbake.req.project) 233*4882a593Smuzhiyun 234*4882a593Smuzhiyun custom_layer_path = self.setup_custom_image_recipe( 235*4882a593Smuzhiyun customrecipe, layers) 236*4882a593Smuzhiyun 237*4882a593Smuzhiyun if os.path.isdir(custom_layer_path): 238*4882a593Smuzhiyun layerlist.append("%03d:%s" % (layer_index,custom_layer_path)) 239*4882a593Smuzhiyun 240*4882a593Smuzhiyun except CustomImageRecipe.DoesNotExist: 241*4882a593Smuzhiyun continue # not a custom recipe, skip 242*4882a593Smuzhiyun 243*4882a593Smuzhiyun layerlist.extend(nongitlayerlist) 244*4882a593Smuzhiyun logger.debug("\n\nset layers gives this list %s" % pformat(layerlist)) 245*4882a593Smuzhiyun self.islayerset = True 246*4882a593Smuzhiyun 247*4882a593Smuzhiyun # restore the order of layer list for bblayers.conf 248*4882a593Smuzhiyun layerlist.sort() 249*4882a593Smuzhiyun sorted_layerlist = [l[4:] for l in layerlist] 250*4882a593Smuzhiyun return sorted_layerlist 251*4882a593Smuzhiyun 252*4882a593Smuzhiyun def setup_custom_image_recipe(self, customrecipe, layers): 253*4882a593Smuzhiyun """ Set up toaster-custom-images layer and recipe files """ 254*4882a593Smuzhiyun layerpath = os.path.join(self.be.builddir, 255*4882a593Smuzhiyun CustomImageRecipe.LAYER_NAME) 256*4882a593Smuzhiyun 257*4882a593Smuzhiyun # create directory structure 258*4882a593Smuzhiyun for name in ("conf", "recipes"): 259*4882a593Smuzhiyun path = os.path.join(layerpath, name) 260*4882a593Smuzhiyun if not os.path.isdir(path): 261*4882a593Smuzhiyun os.makedirs(path) 262*4882a593Smuzhiyun 263*4882a593Smuzhiyun # create layer.conf 264*4882a593Smuzhiyun config = os.path.join(layerpath, "conf", "layer.conf") 265*4882a593Smuzhiyun if not os.path.isfile(config): 266*4882a593Smuzhiyun with open(config, "w") as conf: 267*4882a593Smuzhiyun conf.write('BBPATH .= ":${LAYERDIR}"\nBBFILES += "${LAYERDIR}/recipes/*.bb"\n') 268*4882a593Smuzhiyun 269*4882a593Smuzhiyun # Update the Layer_Version dirpath that has our base_recipe in 270*4882a593Smuzhiyun # to be able to read the base recipe to then generate the 271*4882a593Smuzhiyun # custom recipe. 272*4882a593Smuzhiyun br_layer_base_recipe = layers.get( 273*4882a593Smuzhiyun layer_version=customrecipe.base_recipe.layer_version) 274*4882a593Smuzhiyun 275*4882a593Smuzhiyun # If the layer is one that we've cloned we know where it lives 276*4882a593Smuzhiyun if br_layer_base_recipe.giturl and br_layer_base_recipe.commit: 277*4882a593Smuzhiyun layer_path = self.getGitCloneDirectory( 278*4882a593Smuzhiyun br_layer_base_recipe.giturl, 279*4882a593Smuzhiyun br_layer_base_recipe.commit) 280*4882a593Smuzhiyun # Otherwise it's a local layer 281*4882a593Smuzhiyun elif br_layer_base_recipe.local_source_dir: 282*4882a593Smuzhiyun layer_path = br_layer_base_recipe.local_source_dir 283*4882a593Smuzhiyun else: 284*4882a593Smuzhiyun logger.error("Unable to workout the dir path for the custom" 285*4882a593Smuzhiyun " image recipe") 286*4882a593Smuzhiyun 287*4882a593Smuzhiyun br_layer_base_dirpath = os.path.join( 288*4882a593Smuzhiyun self.be.sourcedir, 289*4882a593Smuzhiyun layer_path, 290*4882a593Smuzhiyun customrecipe.base_recipe.layer_version.dirpath) 291*4882a593Smuzhiyun 292*4882a593Smuzhiyun customrecipe.base_recipe.layer_version.dirpath = br_layer_base_dirpath 293*4882a593Smuzhiyun 294*4882a593Smuzhiyun customrecipe.base_recipe.layer_version.save() 295*4882a593Smuzhiyun 296*4882a593Smuzhiyun # create recipe 297*4882a593Smuzhiyun recipe_path = os.path.join(layerpath, "recipes", "%s.bb" % 298*4882a593Smuzhiyun customrecipe.name) 299*4882a593Smuzhiyun with open(recipe_path, "w") as recipef: 300*4882a593Smuzhiyun recipef.write(customrecipe.generate_recipe_file_contents()) 301*4882a593Smuzhiyun 302*4882a593Smuzhiyun # Update the layer and recipe objects 303*4882a593Smuzhiyun customrecipe.layer_version.dirpath = layerpath 304*4882a593Smuzhiyun customrecipe.layer_version.layer.local_source_dir = layerpath 305*4882a593Smuzhiyun customrecipe.layer_version.layer.save() 306*4882a593Smuzhiyun customrecipe.layer_version.save() 307*4882a593Smuzhiyun 308*4882a593Smuzhiyun customrecipe.file_path = recipe_path 309*4882a593Smuzhiyun customrecipe.save() 310*4882a593Smuzhiyun 311*4882a593Smuzhiyun return layerpath 312*4882a593Smuzhiyun 313*4882a593Smuzhiyun 314*4882a593Smuzhiyun def readServerLogFile(self): 315*4882a593Smuzhiyun return open(os.path.join(self.be.builddir, "toaster_server.log"), "r").read() 316*4882a593Smuzhiyun 317*4882a593Smuzhiyun 318*4882a593Smuzhiyun def triggerBuild(self, bitbake, layers, variables, targets, brbe): 319*4882a593Smuzhiyun layers = self.setLayers(bitbake, layers, targets) 320*4882a593Smuzhiyun is_merged_attr = bitbake.req.project.merged_attr 321*4882a593Smuzhiyun 322*4882a593Smuzhiyun git_env = os.environ.copy() 323*4882a593Smuzhiyun # (note: add custom environment settings here) 324*4882a593Smuzhiyun try: 325*4882a593Smuzhiyun # insure that the project init/build uses the selected bitbake, and not Toaster's 326*4882a593Smuzhiyun del git_env['TEMPLATECONF'] 327*4882a593Smuzhiyun del git_env['BBBASEDIR'] 328*4882a593Smuzhiyun del git_env['BUILDDIR'] 329*4882a593Smuzhiyun except KeyError: 330*4882a593Smuzhiyun pass 331*4882a593Smuzhiyun 332*4882a593Smuzhiyun # init build environment from the clone 333*4882a593Smuzhiyun if bitbake.req.project.builddir: 334*4882a593Smuzhiyun builddir = bitbake.req.project.builddir 335*4882a593Smuzhiyun else: 336*4882a593Smuzhiyun builddir = '%s-toaster-%d' % (self.be.builddir, bitbake.req.project.id) 337*4882a593Smuzhiyun oe_init = os.path.join(self.pokydirname, 'oe-init-build-env') 338*4882a593Smuzhiyun # init build environment 339*4882a593Smuzhiyun try: 340*4882a593Smuzhiyun custom_script = ToasterSetting.objects.get(name="CUSTOM_BUILD_INIT_SCRIPT").value 341*4882a593Smuzhiyun custom_script = custom_script.replace("%BUILDDIR%" ,builddir) 342*4882a593Smuzhiyun self._shellcmd("bash -c 'source %s'" % (custom_script),env=git_env) 343*4882a593Smuzhiyun except ToasterSetting.DoesNotExist: 344*4882a593Smuzhiyun self._shellcmd("bash -c 'source %s %s'" % (oe_init, builddir), 345*4882a593Smuzhiyun self.be.sourcedir,env=git_env) 346*4882a593Smuzhiyun 347*4882a593Smuzhiyun # update bblayers.conf 348*4882a593Smuzhiyun if not is_merged_attr: 349*4882a593Smuzhiyun bblconfpath = os.path.join(builddir, "conf/toaster-bblayers.conf") 350*4882a593Smuzhiyun with open(bblconfpath, 'w') as bblayers: 351*4882a593Smuzhiyun bblayers.write('# line added by toaster build control\n' 352*4882a593Smuzhiyun 'BBLAYERS = "%s"' % ' '.join(layers)) 353*4882a593Smuzhiyun 354*4882a593Smuzhiyun # write configuration file 355*4882a593Smuzhiyun confpath = os.path.join(builddir, 'conf/toaster.conf') 356*4882a593Smuzhiyun with open(confpath, 'w') as conf: 357*4882a593Smuzhiyun for var in variables: 358*4882a593Smuzhiyun conf.write('%s="%s"\n' % (var.name, var.value)) 359*4882a593Smuzhiyun conf.write('INHERIT+="toaster buildhistory"') 360*4882a593Smuzhiyun else: 361*4882a593Smuzhiyun # Append the Toaster-specific values directly to the bblayers.conf 362*4882a593Smuzhiyun bblconfpath = os.path.join(builddir, "conf/bblayers.conf") 363*4882a593Smuzhiyun bblconfpath_save = os.path.join(builddir, "conf/bblayers.conf.save") 364*4882a593Smuzhiyun shutil.copyfile(bblconfpath, bblconfpath_save) 365*4882a593Smuzhiyun with open(bblconfpath) as bblayers: 366*4882a593Smuzhiyun content = bblayers.readlines() 367*4882a593Smuzhiyun do_write = True 368*4882a593Smuzhiyun was_toaster = False 369*4882a593Smuzhiyun with open(bblconfpath,'w') as bblayers: 370*4882a593Smuzhiyun for line in content: 371*4882a593Smuzhiyun #line = line.strip('\n') 372*4882a593Smuzhiyun if 'TOASTER_CONFIG_PROLOG' in line: 373*4882a593Smuzhiyun do_write = False 374*4882a593Smuzhiyun was_toaster = True 375*4882a593Smuzhiyun elif 'TOASTER_CONFIG_EPILOG' in line: 376*4882a593Smuzhiyun do_write = True 377*4882a593Smuzhiyun elif do_write: 378*4882a593Smuzhiyun bblayers.write(line) 379*4882a593Smuzhiyun if not was_toaster: 380*4882a593Smuzhiyun bblayers.write('\n') 381*4882a593Smuzhiyun bblayers.write('#=== TOASTER_CONFIG_PROLOG ===\n') 382*4882a593Smuzhiyun bblayers.write('BBLAYERS = "\\\n') 383*4882a593Smuzhiyun for layer in layers: 384*4882a593Smuzhiyun bblayers.write(' %s \\\n' % layer) 385*4882a593Smuzhiyun bblayers.write(' "\n') 386*4882a593Smuzhiyun bblayers.write('#=== TOASTER_CONFIG_EPILOG ===\n') 387*4882a593Smuzhiyun # Append the Toaster-specific values directly to the local.conf 388*4882a593Smuzhiyun bbconfpath = os.path.join(builddir, "conf/local.conf") 389*4882a593Smuzhiyun bbconfpath_save = os.path.join(builddir, "conf/local.conf.save") 390*4882a593Smuzhiyun shutil.copyfile(bbconfpath, bbconfpath_save) 391*4882a593Smuzhiyun with open(bbconfpath) as f: 392*4882a593Smuzhiyun content = f.readlines() 393*4882a593Smuzhiyun do_write = True 394*4882a593Smuzhiyun was_toaster = False 395*4882a593Smuzhiyun with open(bbconfpath,'w') as conf: 396*4882a593Smuzhiyun for line in content: 397*4882a593Smuzhiyun #line = line.strip('\n') 398*4882a593Smuzhiyun if 'TOASTER_CONFIG_PROLOG' in line: 399*4882a593Smuzhiyun do_write = False 400*4882a593Smuzhiyun was_toaster = True 401*4882a593Smuzhiyun elif 'TOASTER_CONFIG_EPILOG' in line: 402*4882a593Smuzhiyun do_write = True 403*4882a593Smuzhiyun elif do_write: 404*4882a593Smuzhiyun conf.write(line) 405*4882a593Smuzhiyun if not was_toaster: 406*4882a593Smuzhiyun conf.write('\n') 407*4882a593Smuzhiyun conf.write('#=== TOASTER_CONFIG_PROLOG ===\n') 408*4882a593Smuzhiyun for var in variables: 409*4882a593Smuzhiyun if (not var.name.startswith("INTERNAL_")) and (not var.name == "BBLAYERS"): 410*4882a593Smuzhiyun conf.write('%s="%s"\n' % (var.name, var.value)) 411*4882a593Smuzhiyun conf.write('#=== TOASTER_CONFIG_EPILOG ===\n') 412*4882a593Smuzhiyun 413*4882a593Smuzhiyun # If 'target' is just the project preparation target, then we are done 414*4882a593Smuzhiyun for target in targets: 415*4882a593Smuzhiyun if "_PROJECT_PREPARE_" == target.target: 416*4882a593Smuzhiyun logger.debug('localhostbecontroller: Project has been prepared. Done.') 417*4882a593Smuzhiyun # Update the Build Request and release the build environment 418*4882a593Smuzhiyun bitbake.req.state = BuildRequest.REQ_COMPLETED 419*4882a593Smuzhiyun bitbake.req.save() 420*4882a593Smuzhiyun self.be.lock = BuildEnvironment.LOCK_FREE 421*4882a593Smuzhiyun self.be.save() 422*4882a593Smuzhiyun # Close the project build and progress bar 423*4882a593Smuzhiyun bitbake.req.build.outcome = Build.SUCCEEDED 424*4882a593Smuzhiyun bitbake.req.build.save() 425*4882a593Smuzhiyun # Update the project status 426*4882a593Smuzhiyun bitbake.req.project.set_variable(Project.PROJECT_SPECIFIC_STATUS,Project.PROJECT_SPECIFIC_CLONING_SUCCESS) 427*4882a593Smuzhiyun signal_runbuilds() 428*4882a593Smuzhiyun return 429*4882a593Smuzhiyun 430*4882a593Smuzhiyun # clean the Toaster to build environment 431*4882a593Smuzhiyun env_clean = 'unset BBPATH;' # clean BBPATH for <= YP-2.4.0 432*4882a593Smuzhiyun 433*4882a593Smuzhiyun # run bitbake server from the clone if available 434*4882a593Smuzhiyun # otherwise pick it from the PATH 435*4882a593Smuzhiyun bitbake = os.path.join(self.pokydirname, 'bitbake', 'bin', 'bitbake') 436*4882a593Smuzhiyun if not os.path.exists(bitbake): 437*4882a593Smuzhiyun logger.info("Bitbake not available under %s, will try to use it from PATH" % 438*4882a593Smuzhiyun self.pokydirname) 439*4882a593Smuzhiyun for path in os.environ["PATH"].split(os.pathsep): 440*4882a593Smuzhiyun if os.path.exists(os.path.join(path, 'bitbake')): 441*4882a593Smuzhiyun bitbake = os.path.join(path, 'bitbake') 442*4882a593Smuzhiyun break 443*4882a593Smuzhiyun else: 444*4882a593Smuzhiyun logger.error("Looks like Bitbake is not available, please fix your environment") 445*4882a593Smuzhiyun 446*4882a593Smuzhiyun toasterlayers = os.path.join(builddir,"conf/toaster-bblayers.conf") 447*4882a593Smuzhiyun if not is_merged_attr: 448*4882a593Smuzhiyun self._shellcmd('%s bash -c \"source %s %s; BITBAKE_UI="knotty" %s --read %s --read %s ' 449*4882a593Smuzhiyun '--server-only -B 0.0.0.0:0\"' % (env_clean, oe_init, 450*4882a593Smuzhiyun builddir, bitbake, confpath, toasterlayers), self.be.sourcedir) 451*4882a593Smuzhiyun else: 452*4882a593Smuzhiyun self._shellcmd('%s bash -c \"source %s %s; BITBAKE_UI="knotty" %s ' 453*4882a593Smuzhiyun '--server-only -B 0.0.0.0:0\"' % (env_clean, oe_init, 454*4882a593Smuzhiyun builddir, bitbake), self.be.sourcedir) 455*4882a593Smuzhiyun 456*4882a593Smuzhiyun # read port number from bitbake.lock 457*4882a593Smuzhiyun self.be.bbport = -1 458*4882a593Smuzhiyun bblock = os.path.join(builddir, 'bitbake.lock') 459*4882a593Smuzhiyun # allow 10 seconds for bb lock file to appear but also be populated 460*4882a593Smuzhiyun for lock_check in range(10): 461*4882a593Smuzhiyun if not os.path.exists(bblock): 462*4882a593Smuzhiyun logger.debug("localhostbecontroller: waiting for bblock file to appear") 463*4882a593Smuzhiyun time.sleep(1) 464*4882a593Smuzhiyun continue 465*4882a593Smuzhiyun if 10 < os.stat(bblock).st_size: 466*4882a593Smuzhiyun break 467*4882a593Smuzhiyun logger.debug("localhostbecontroller: waiting for bblock content to appear") 468*4882a593Smuzhiyun time.sleep(1) 469*4882a593Smuzhiyun else: 470*4882a593Smuzhiyun raise BuildSetupException("Cannot find bitbake server lock file '%s'. Exiting." % bblock) 471*4882a593Smuzhiyun 472*4882a593Smuzhiyun with open(bblock) as fplock: 473*4882a593Smuzhiyun for line in fplock: 474*4882a593Smuzhiyun if ":" in line: 475*4882a593Smuzhiyun self.be.bbport = line.split(":")[-1].strip() 476*4882a593Smuzhiyun logger.debug("localhostbecontroller: bitbake port %s", self.be.bbport) 477*4882a593Smuzhiyun break 478*4882a593Smuzhiyun 479*4882a593Smuzhiyun if -1 == self.be.bbport: 480*4882a593Smuzhiyun raise BuildSetupException("localhostbecontroller: can't read bitbake port from %s" % bblock) 481*4882a593Smuzhiyun 482*4882a593Smuzhiyun self.be.bbaddress = "localhost" 483*4882a593Smuzhiyun self.be.bbstate = BuildEnvironment.SERVER_STARTED 484*4882a593Smuzhiyun self.be.lock = BuildEnvironment.LOCK_RUNNING 485*4882a593Smuzhiyun self.be.save() 486*4882a593Smuzhiyun 487*4882a593Smuzhiyun bbtargets = '' 488*4882a593Smuzhiyun for target in targets: 489*4882a593Smuzhiyun task = target.task 490*4882a593Smuzhiyun if task: 491*4882a593Smuzhiyun if not task.startswith('do_'): 492*4882a593Smuzhiyun task = 'do_' + task 493*4882a593Smuzhiyun task = ':%s' % task 494*4882a593Smuzhiyun bbtargets += '%s%s ' % (target.target, task) 495*4882a593Smuzhiyun 496*4882a593Smuzhiyun # run build with local bitbake. stop the server after the build. 497*4882a593Smuzhiyun log = os.path.join(builddir, 'toaster_ui.log') 498*4882a593Smuzhiyun local_bitbake = os.path.join(os.path.dirname(os.getenv('BBBASEDIR')), 499*4882a593Smuzhiyun 'bitbake') 500*4882a593Smuzhiyun if not is_merged_attr: 501*4882a593Smuzhiyun self._shellcmd(['%s bash -c \"(TOASTER_BRBE="%s" BBSERVER="0.0.0.0:%s" ' 502*4882a593Smuzhiyun '%s %s -u toasterui --read %s --read %s --token="" >>%s 2>&1;' 503*4882a593Smuzhiyun 'BITBAKE_UI="knotty" BBSERVER=0.0.0.0:%s %s -m)&\"' \ 504*4882a593Smuzhiyun % (env_clean, brbe, self.be.bbport, local_bitbake, bbtargets, confpath, toasterlayers, log, 505*4882a593Smuzhiyun self.be.bbport, bitbake,)], 506*4882a593Smuzhiyun builddir, nowait=True) 507*4882a593Smuzhiyun else: 508*4882a593Smuzhiyun self._shellcmd(['%s bash -c \"(TOASTER_BRBE="%s" BBSERVER="0.0.0.0:%s" ' 509*4882a593Smuzhiyun '%s %s -u toasterui --token="" >>%s 2>&1;' 510*4882a593Smuzhiyun 'BITBAKE_UI="knotty" BBSERVER=0.0.0.0:%s %s -m)&\"' \ 511*4882a593Smuzhiyun % (env_clean, brbe, self.be.bbport, local_bitbake, bbtargets, log, 512*4882a593Smuzhiyun self.be.bbport, bitbake,)], 513*4882a593Smuzhiyun builddir, nowait=True) 514*4882a593Smuzhiyun 515*4882a593Smuzhiyun logger.debug('localhostbecontroller: Build launched, exiting. ' 516*4882a593Smuzhiyun 'Follow build logs at %s' % log) 517