Save env used on configure
[deliverable/lttng-ivc.git] / lttng_ivc / utils / project.py
CommitLineData
efdd48db
JR
1# Copyright (c) 2017 Jonathan Rajotte-Julien <jonathan.rajotte-julien@efficios.com>
2#
3# Permission is hereby granted, free of charge, to any person obtaining a copy
4# of this software and associated documentation files (the "Software"), to deal
5# in the Software without restriction, including without limitation the rights
6# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7# copies of the Software, and to permit persons to whom the Software is
8# furnished to do so, subject to the following conditions:
9#
10# The above copyright notice and this permission notice shall be included in all
11# copies or substantial portions of the Software.
12#
13# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19# SOFTWARE.
20
de9b991b
JR
21import os
22import shutil
23import git
24import subprocess
25import logging
cb87ff89 26import lttng_ivc.settings as Settings
5908717c 27import pprint
cb87ff89
JR
28
29from lttng_ivc.utils.utils import sha256_checksum
816f8cc1 30from lttng_ivc.utils.utils import find_dir, find_file
de9b991b 31
fe7b987e 32_logger = logging.getLogger('project')
de9b991b
JR
33
34class Project(object):
35
36 def __init__(self, label, git_path, sha1, tmpdir):
37 self.label = label
38 self.git_path = git_path
39 self.sha1 = sha1
40
41 """ Custom configure flags in the for of ['-x', 'arg']"""
42 self.custom_configure_flags = []
43 ccache = shutil.which("ccache")
44 if ccache is not None:
45 self.custom_configure_flags.append("CC={} gcc".format(ccache))
46 self.custom_configure_flags.append("CXX={} g++".format(ccache))
97d494b0 47 self.custom_configure_flags.append("CFLAGS=-g -O0".format(ccache))
de9b991b
JR
48
49 """ A collection of Project dependencies """
fe7b987e 50 self.dependencies = {}
cb87ff89 51 # used for project cache and pickle validation
fe7b987e 52 self._immutable = False
cb87ff89 53 self._py_file_checksum = sha256_checksum(Settings.project_py_file_location)
de9b991b
JR
54
55 # State
de9b991b
JR
56 self.isBuilt = False
57 self.isConfigured = False
58 self.isInstalled = False
ab63b97e 59 self.skip = False
de9b991b 60
fe7b987e
JR
61 self.basedir = tmpdir
62 self.log_path = os.path.join(tmpdir, "log")
63 self.source_path = os.path.join(tmpdir, "source")
64 self.installation_path = os.path.join(tmpdir, "install")
65
97d494b0
JR
66 if os.path.isdir(self.basedir):
67 # Perform cleanup since it should not happen
68 shutil.rmtree(self.basedir)
69
fe7b987e 70 os.makedirs(self.log_path)
de9b991b
JR
71 os.makedirs(self.source_path)
72 os.makedirs(self.installation_path)
de9b991b
JR
73
74 self.special_env_variables = {}
75
76 # Init the repo for work
77 self.checkout()
78 self.bootstrap()
79
18aedaf9
JR
80 def add_special_env_variable(self, key, value):
81 if key in self.special_env_variables:
82 _logger.warning("{} Special var {} is already defined".format(
83 self.label, key))
84 raise Exception("Multiple definition of a special environment variable")
85 self.special_env_variables[key] = value
86
de9b991b 87 def get_cppflags(self):
18aedaf9
JR
88 cppflags = ["-I{}/include".format(self.installation_path)]
89 for key, dep in self.dependencies.items():
90 cppflags.append(dep.get_cppflags())
91
92 return " ".join(cppflags)
de9b991b
JR
93
94 def get_ldflags(self):
18aedaf9
JR
95 ldflags = ["-L{}/lib".format(self.installation_path)]
96 for key, dep in self.dependencies.items():
97 ldflags.append(dep.get_ldflags())
98 return " ".join(ldflags)
de9b991b
JR
99
100 def get_ld_library_path(self):
18aedaf9
JR
101 library_path = ["{}/lib".format(self.installation_path)]
102 for key, dep in self.dependencies.items():
103 library_path.append(dep.get_ld_library_path())
104 return ":".join(library_path)
105
106 def get_bin_path(self):
107 bin_path = ["{}/bin".format(self.installation_path)]
108 for key, dep in self.dependencies.items():
109 bin_path.append(dep.get_bin_path())
110 return ":".join(bin_path)
de9b991b
JR
111
112 def get_env(self):
113 """Modify environment to reflect dependency"""
18aedaf9
JR
114 env_var = {"CPPFLAGS": (self.get_cppflags(), " "),
115 "LDFLAGS": (self.get_ldflags(), " "),
116 "LD_LIBRARY_PATH": (self.get_ld_library_path(), ":"),
117 }
de9b991b
JR
118
119 env = os.environ.copy()
120
121 for var, value in self.special_env_variables.items():
122 if var in env:
de9b991b 123 # Raise for now since no special cases is known
18aedaf9
JR
124 _logger.warning("{} Special var {} is already defined".format(
125 self.label, var))
de9b991b
JR
126 raise Exception("Multiple definition of a special environment variable")
127 else:
128 env[var] = value
129
fe7b987e 130 for key, dep in self.dependencies.items():
de9b991b 131 # Extra space just in case
de9b991b
JR
132 for var, value in dep.special_env_variables.items():
133 if var in env:
de9b991b 134 # Raise for now since no special cases is known
18aedaf9
JR
135 _logger.warning("{} Special var {} is already defined".format(
136 self.label, var))
de9b991b
JR
137 raise Exception("Multiple definition of a special environment variable")
138 else:
139 env[var] = value
140
18aedaf9
JR
141 for var, (value, delimiter) in env_var.items():
142 tmp = [value]
143 if var in env:
144 tmp.append(env[var])
145 env[var] = delimiter.join(tmp)
146
de9b991b
JR
147 return env
148
149 def autobuild(self):
150 """
151 Perform the bootstrap, configuration, build and install the
152 project. Build dependencies if not already built
153 """
fe7b987e
JR
154 if (self.isConfigured and self.isBuilt and self.isInstalled):
155 return
156
157 if self._immutable:
158 raise Exception("Object is immutable. Illegal autobuild")
159
160 for key, dep in self.dependencies.items():
de9b991b
JR
161 dep.autobuild()
162
fe7b987e 163 if self.isConfigured ^ self.isBuilt ^ self.isInstalled:
de9b991b
JR
164 raise Exception("Project steps where manually triggered. Can't autobuild")
165
97d494b0
JR
166 _logger.debug("{} Autobuild configure".format(self.label))
167 try:
168 self.configure()
169 except subprocess.CalledProcessError as e:
170 _logger.error("{} Configure failed. See {} for more details.".format(self.label, self.log_path))
171 raise e
172
173 _logger.debug("{} Autobuild build".format(self.label))
174 try:
175 self.build()
176 except subprocess.CalledProcessError as e:
177 _logger.error("{} Build failed. See {} for more details.".format(self.label, self.log_path))
178 raise e
179
180 _logger.debug("{} Autobuild install".format(self.label))
181 try:
182 self.install()
183 except subprocess.CalledProcessError as e:
184 _logger.error("{} Install failed. See {} for more details.".format(self.label, self.log_path))
185 raise e
de9b991b
JR
186
187 def checkout(self):
fe7b987e
JR
188 if self._immutable:
189 raise Exception("Object is immutable. Illegal checkout")
190
de9b991b
JR
191 repo = git.Repo.clone_from(self.git_path, self.source_path)
192 commit = repo.commit(self.sha1)
193 repo.head.reference = commit
194 assert repo.head.is_detached
195 repo.head.reset(index=True, working_tree=True)
196
197 def bootstrap(self):
198 """
199 Bootstap the project. Raise subprocess.CalledProcessError on
200 bootstrap error.
201 """
fe7b987e
JR
202 if self._immutable:
203 raise Exception("Object is immutable. Illegal bootstrap")
204
205 out = os.path.join(self.log_path, "bootstrap.out")
206 err = os.path.join(self.log_path, "bootstrap.err")
207
de9b991b 208 os.chdir(self.source_path)
fe7b987e
JR
209 with open(out, 'w') as stdout, open(err, 'w') as stderr:
210 p = subprocess.run(['./bootstrap'], stdout=stdout, stderr=stderr)
de9b991b
JR
211 p.check_returncode()
212 return p
213
214 def configure(self):
215 """
216 Configure the project.
217 Raises subprocess.CalledProcessError on configure error
218 """
fe7b987e
JR
219 if self._immutable:
220 raise Exception("Object is immutable. Illegal configure")
221
de9b991b 222 # Check that all our dependencies were actually installed
fe7b987e 223 for key, dep in self.dependencies.items():
de9b991b
JR
224 if not dep.isInstalled:
225 # TODO: Custom exception here Dependency Error
226 raise Exception("Dependency project flagged as not installed")
227
fe7b987e
JR
228 out = os.path.join(self.log_path, "configure.out")
229 err = os.path.join(self.log_path, "configure.err")
5908717c
JR
230 env_file = os.path.join(self.log_path, "configure.env")
231
232 env = self.get_env()
233
234 with open(env_file, 'w') as tmp:
235 pprint.pprint(env, stream=tmp)
fe7b987e 236
de9b991b
JR
237 os.chdir(self.source_path)
238 args = ['./configure']
239 prefix = '--prefix={}'.format(self.installation_path)
240 args.append(prefix)
241 args.extend(self.custom_configure_flags)
242
243 # TODO: log output and add INFO log point
fe7b987e 244 with open(out, 'w') as stdout, open(err, 'w') as stderr:
5908717c 245 p = subprocess.run(args, env=env, stdout=stdout,
fe7b987e 246 stderr=stderr)
de9b991b
JR
247 p.check_returncode()
248 self.isConfigured = True
249 return p
250
251 def build(self):
252 """
253 Build the project. Raise subprocess.CalledProcessError on build
254 error.
255 """
fe7b987e
JR
256 if self._immutable:
257 raise Exception("Object is immutable. Illegal build")
258
259 out = os.path.join(self.log_path, "build.out")
260 err = os.path.join(self.log_path, "build.err")
261
de9b991b
JR
262 os.chdir(self.source_path)
263 args = ['make']
264 env = self.get_env()
de9b991b
JR
265
266 # Number of usable cpu
267 # https://docs.python.org/3/library/os.html#os.cpu_count
268 num_cpu = str(len(os.sched_getaffinity(0)))
269 args.append('-j')
270 args.append(num_cpu)
97d494b0 271 args.append('V=1')
de9b991b
JR
272
273 # TODO: log output and add INFO log point with args
fe7b987e
JR
274 with open(out, 'w') as stdout, open(err, 'w') as stderr:
275 p = subprocess.run(args, env=env, stdout=stdout,
276 stderr=stderr)
de9b991b
JR
277 p.check_returncode()
278 self.isBuilt = True
279 return p
280
281 def install(self):
282 """
283 Install the project. Raise subprocess.CalledProcessError on
284 bootstrap error
285 """
fe7b987e
JR
286 if self._immutable:
287 raise Exception("Object is immutable. Illegal install")
288
97d494b0
JR
289 out = os.path.join(self.log_path, "install.out")
290 err = os.path.join(self.log_path, "install.err")
fe7b987e 291
de9b991b
JR
292 os.chdir(self.source_path)
293 args = ['make', 'install']
294
295 # TODO: log output and add INFO log point
fe7b987e
JR
296 with open(out, 'w') as stdout, open(err, 'w') as stderr:
297 p = subprocess.run(args, env=self.get_env(), stdout=stdout,
298 stderr=stderr)
de9b991b
JR
299 p.check_returncode()
300 self.isInstalled = True
301 return p
302
303 def cleanup(self):
304 if os.path.exists(self.source_path):
305 shutil.rmtree(self.source_path)
306 if os.path.exists(self.installation_path):
307 shutil.rmtree(self.installation_path)
308
309
310class Lttng_modules(Project):
97d494b0
JR
311 def __init__(self, label, git_path, sha1, tmpdir):
312 super(Lttng_modules, self).__init__(label=label, git_path=git_path,
313 sha1=sha1, tmpdir=tmpdir)
f0acf3f3 314 self.add_special_env_variable("MODPROBE_OPTIONS","-d {}".format(self.installation_path))
97d494b0 315
de9b991b
JR
316 def bootstrap(self):
317 pass
318
319 def configure(self):
320 pass
321
322 def install(self):
fe7b987e
JR
323 if self._immutable:
324 raise Exception("Object is immutable. Illegal install")
de9b991b
JR
325 os.chdir(self.source_path)
326 args = ['make', 'INSTALL_MOD_PATH={}'.format(self.installation_path),
327 'modules_install']
328 p = subprocess.run(args, env=self.get_env(), stdout=subprocess.PIPE,
329 stderr=subprocess.PIPE)
330 p.check_returncode()
331
332 # Perform a local depmod
333 args = ['depmod', '-b', self.installation_path]
334 p = subprocess.run(args, env=self.get_env())
335 p.check_returncode()
336 self.isInstalled = True
337
2dd375bf
JR
338 def autobuild(self):
339 try:
340 super(Lttng_modules, self).autobuild()
341 except subprocess.CalledProcessError as e:
342 self.skip = True
343
de9b991b
JR
344
345class Lttng_ust(Project):
346 def __init__(self, label, git_path, sha1, tmpdir):
347 super(Lttng_ust, self).__init__(label=label, git_path=git_path,
348 sha1=sha1, tmpdir=tmpdir)
349 self.custom_configure_flags.extend(['--disable-man-pages'])
e5cbfcca
JR
350 self.custom_configure_flags.extend(['--enable-python-agent'])
351 self.custom_configure_flags.extend(['--enable-java-agent-jul'])
352
74eb7096
JR
353 jul_path = os.path.join(self.installation_path,
354 "share/java/liblttng-ust-agent.jar")
8e2c5f79 355 classpath = ":".join([jul_path, '.'])
74eb7096 356 self.add_special_env_variable("CLASSPATH", classpath)
de9b991b 357
2094d672
JR
358 def install(self):
359 super(Lttng_ust, self).install()
360 python_path = find_dir(self.installation_path, "lttngust")
361 if python_path:
362 # Fetch the parent of lttngust folder
363 python_path = os.path.dirname(python_path)
364 self.add_special_env_variable("PYTHONPATH", python_path)
365
366
de9b991b
JR
367
368class Lttng_tools(Project):
18aedaf9
JR
369 def __init__(self, label, git_path, sha1, tmpdir):
370 super(Lttng_tools, self).__init__(label=label, git_path=git_path,
371 sha1=sha1, tmpdir=tmpdir)
372 self.add_special_env_variable("LTTNG_SESSION_CONFIG_XSD_PATH",
373 os.path.join(self.installation_path, "share/xml/lttng/"))
de9b991b 374
816f8cc1
JR
375 # Find the mi xsd
376 for xsd in Settings.mi_xsd_file_name:
377 mi = find_file(self.source_path, xsd)
378 if mi:
379 break
380 if not mi:
381 raise Exception("MI xsd not found")
382 self.mi_xsd = mi
383
de9b991b
JR
384
385class Babeltrace(Project):
386 pass
This page took 0.043665 seconds and 5 git commands to generate.