blob: b2c573c9eb8744d4498fb9bb7625e8be6666a932 [file] [log] [blame]
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001from django.core.management.base import NoArgsCommand, CommandError
2from django.db import transaction
3from bldcontrol.bbcontroller import getBuildEnvironmentController, ShellCmdException
4from bldcontrol.models import BuildRequest, BuildEnvironment, BRError
5from orm.models import ToasterSetting, Build
6import os
7import sys, traceback
8
9def DN(path):
10 if path is None:
11 return ""
12 else:
13 return os.path.dirname(path)
14
15
16class Command(NoArgsCommand):
17 args = ""
18 help = "Verifies that the configured settings are valid and usable, or prompts the user to fix the settings."
19
20 def __init__(self, *args, **kwargs):
21 super(Command, self).__init__(*args, **kwargs)
22 self.guesspath = DN(DN(DN(DN(DN(DN(DN(__file__)))))))
23
24 def _find_first_path_for_file(self, startdirectory, filename, level = 0):
25 if level < 0:
26 return None
27 dirs = []
28 for i in os.listdir(startdirectory):
29 j = os.path.join(startdirectory, i)
30 if os.path.isfile(j):
31 if i == filename:
32 return startdirectory
33 elif os.path.isdir(j):
34 dirs.append(j)
35 for j in dirs:
36 ret = self._find_first_path_for_file(j, filename, level - 1)
37 if ret is not None:
38 return ret
39 return None
40
41 def _recursive_list_directories(self, startdirectory, level = 0):
42 if level < 0:
43 return []
44 dirs = []
45 try:
46 for i in os.listdir(startdirectory):
47 j = os.path.join(startdirectory, i)
48 if os.path.isdir(j):
49 dirs.append(j)
50 except OSError:
51 pass
52 for j in dirs:
53 dirs = dirs + self._recursive_list_directories(j, level - 1)
54 return dirs
55
56
57 def _get_suggested_sourcedir(self, be):
58 if be.betype != BuildEnvironment.TYPE_LOCAL:
59 return ""
60 return DN(DN(DN(self._find_first_path_for_file(self.guesspath, "toasterconf.json", 4))))
61
62 def _get_suggested_builddir(self, be):
63 if be.betype != BuildEnvironment.TYPE_LOCAL:
64 return ""
65 return DN(self._find_first_path_for_file(DN(self.guesspath), "bblayers.conf", 4))
66
Patrick Williamsc124f4f2015-09-15 14:41:29 -050067 def _verify_build_environment(self):
68 # refuse to start if we have no build environments
69 while BuildEnvironment.objects.count() == 0:
70 print(" !! No build environments found. Toaster needs at least one build environment in order to be able to run builds.\n" +
71 "You can manually define build environments in the database table bldcontrol_buildenvironment.\n" +
72 "Or Toaster can define a simple localhost-based build environment for you.")
73
74 i = raw_input(" -- Do you want to create a basic localhost build environment ? (Y/n) ");
75 if not len(i) or i.startswith("y") or i.startswith("Y"):
76 BuildEnvironment.objects.create(pk = 1, betype = 0)
77 else:
78 raise Exception("Toaster cannot start without build environments. Aborting.")
79
80
81 # we make sure we have builddir and sourcedir for all defined build envionments
82 for be in BuildEnvironment.objects.all():
83 be.needs_import = False
84 def _verify_be():
85 is_changed = False
86 print("\nVerifying the build environment. If the local build environment is not properly configured, you will be asked to configure it.")
87
88 def _update_sourcedir():
89 suggesteddir = self._get_suggested_sourcedir(be)
90 if len(suggesteddir) > 0:
91 be.sourcedir = raw_input("This is the directory Toaster uses to check out the source code of the layers you will build. Toaster will create new clones of the layers, so existing content in the chosen directory will not be changed.\nToaster suggests you use \"%s\" as your layers checkout directory. If you select this directory, a layer like \"meta-intel\" will end up in \"%s/meta-intel\".\nPress Enter to select \"%s\" or type the full path to a different directory. If you provide your own directory, it must be a parent of the cloned directory for the sources you are using to run Toaster: " % (suggesteddir, suggesteddir, suggesteddir))
92 else:
93 be.sourcedir = raw_input("Toaster needs to know in which directory it should check out the source code of the layers you will build. The directory should be a parent of the cloned directory for the sources you are using to run Toaster. Toaster will create new clones of the layers, so existing content in the chosen directory will not be changed.\nType the full path to the directory (for example: \"%s\": " % os.environ.get('HOME', '/tmp/'))
94 if len(be.sourcedir) == 0 and len(suggesteddir) > 0:
95 be.sourcedir = suggesteddir
96 return True
97
98 if len(be.sourcedir) == 0:
99 print "\n -- Validation: The layers checkout directory must be set."
100 is_changed = _update_sourcedir()
101
102 if not be.sourcedir.startswith("/"):
103 print "\n -- Validation: The layers checkout directory must be set to an absolute path."
104 is_changed = _update_sourcedir()
105
106 if not be.sourcedir in DN(__file__):
107 print "\n -- Validation: The layers checkout directory must be a parent of the current checkout."
108 is_changed = _update_sourcedir()
109
110 if is_changed:
111 if be.betype == BuildEnvironment.TYPE_LOCAL:
112 be.needs_import = True
113 return True
114
115 def _update_builddir():
116 suggesteddir = self._get_suggested_builddir(be)
117 if len(suggesteddir) > 0:
118 be.builddir = raw_input("Toaster needs to know where your build directory is located.\nThe build directory is where all the artifacts created by your builds will be stored. Toaster suggests \"%s\".\nPress Enter to select \"%s\" or type the full path to a different directory: " % (suggesteddir, suggesteddir))
119 else:
120 be.builddir = raw_input("Toaster needs to know where is your build directory.\nThe build directory is where all the artifacts created by your builds will be stored. Type the full path to the directory (for example: \" %s/build\")" % os.environ.get('HOME','/tmp/'))
121 if len(be.builddir) == 0 and len(suggesteddir) > 0:
122 be.builddir = suggesteddir
123 return True
124
125 if len(be.builddir) == 0:
126 print "\n -- Validation: The build directory must be set."
127 is_changed = _update_builddir()
128
129 if not be.builddir.startswith("/"):
130 print "\n -- Validation: The build directory must to be set to an absolute path."
131 is_changed = _update_builddir()
132
133
134 if is_changed:
135 print "\nBuild configuration saved"
136 be.save()
137 return True
138
139
140 if be.needs_import:
141 print "\nToaster can use a SINGLE predefined configuration file to set up default project settings and layer information sources.\n"
142
143 # find configuration files
144 config_files = []
145 for dirname in self._recursive_list_directories(be.sourcedir,2):
146 if os.path.exists(os.path.join(dirname, ".templateconf")):
147 import subprocess
148 proc = subprocess.Popen('bash -c ". '+os.path.join(dirname, ".templateconf")+'; echo \"\$TEMPLATECONF\""', shell=True, stdout=subprocess.PIPE)
149 conffilepath, stderroroutput = proc.communicate()
150 proc.wait()
151 if proc.returncode != 0:
152 raise Exception("Failed to source TEMPLATECONF: %s" % stderroroutput)
153
154 conffilepath = os.path.join(conffilepath.strip(), "toasterconf.json")
155 candidatefilepath = os.path.join(dirname, conffilepath)
156 if "toaster_cloned" in candidatefilepath:
157 continue
158 if os.path.exists(candidatefilepath):
159 config_files.append(candidatefilepath)
160
161 if len(config_files) > 0:
162 print "Toaster will list now the configuration files that it found. Select the number to use the desired configuration file."
163 for cf in config_files:
164 print " [%d] - %s" % (config_files.index(cf) + 1, cf)
165 print "\n [0] - Exit without importing any file"
166 try:
167 i = raw_input("\nEnter your option: ")
168 if len(i) and (int(i) - 1 >= 0 and int(i) - 1 < len(config_files)):
169 print "\nImporting file: %s" % config_files[int(i)-1]
170 from loadconf import Command as LoadConfigCommand
171
172 LoadConfigCommand()._import_layer_config(config_files[int(i)-1])
173 # we run lsupdates after config update
174 print "\nLayer configuration imported. Updating information from the layer sources, please wait.\nYou can re-update any time later by running bitbake/lib/toaster/manage.py lsupdates"
175 from django.core.management import call_command
176 call_command("lsupdates")
177
178 # we don't look for any other config files
179 return is_changed
180 except Exception as e:
181 print "Failure while trying to import the toaster config file: %s" % e
182 traceback.print_exc(e)
183 else:
184 print "\nToaster could not find a configuration file. You need to configure Toaster manually using the web interface, or create a configuration file and use\n bitbake/lib/toaster/managepy.py loadconf [filename]\n command to load it. You can use https://wiki.yoctoproject.org/wiki/File:Toasterconf.json.txt.patch as a starting point."
185
186
187
188
189 return is_changed
190
191 while (_verify_be()):
192 pass
193 return 0
194
195 def _verify_default_settings(self):
196 # verify that default settings are there
197 if ToasterSetting.objects.filter(name = 'DEFAULT_RELEASE').count() != 1:
198 ToasterSetting.objects.filter(name = 'DEFAULT_RELEASE').delete()
199 ToasterSetting.objects.get_or_create(name = 'DEFAULT_RELEASE', value = '')
200 return 0
201
202 def _verify_builds_in_progress(self):
203 # we are just starting up. we must not have any builds in progress, or build environments taken
204 for b in BuildRequest.objects.filter(state = BuildRequest.REQ_INPROGRESS):
205 BRError.objects.create(req = b, errtype = "toaster", errmsg = "Toaster found this build IN PROGRESS while Toaster started up. This is an inconsistent state, and the build was marked as failed")
206
207 BuildRequest.objects.filter(state = BuildRequest.REQ_INPROGRESS).update(state = BuildRequest.REQ_FAILED)
208
209 BuildEnvironment.objects.update(lock = BuildEnvironment.LOCK_FREE)
210
211 # also mark "In Progress builds as failures"
212 from django.utils import timezone
213 Build.objects.filter(outcome = Build.IN_PROGRESS).update(outcome = Build.FAILED, completed_on = timezone.now())
214
215 return 0
216
217
218
219 def handle_noargs(self, **options):
220 retval = 0
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500221 retval += self._verify_build_environment()
222 retval += self._verify_default_settings()
223 retval += self._verify_builds_in_progress()
224
225 return retval