LD_LIBRARY_PATH can already be defined in the runtime environment
[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:
ca2f2d59
JR
123 if var == "LD_LIBRARY_PATH":
124 env[var] = ":".join([env[var], value])
125 else:
126 _logger.warning("{} Special var {} is already defined".format(
127 self.label, var))
128 raise Exception("Multiple definition of a special environment variable")
de9b991b
JR
129 else:
130 env[var] = value
131
fe7b987e 132 for key, dep in self.dependencies.items():
de9b991b 133 # Extra space just in case
de9b991b
JR
134 for var, value in dep.special_env_variables.items():
135 if var in env:
de9b991b 136 # Raise for now since no special cases is known
18aedaf9
JR
137 _logger.warning("{} Special var {} is already defined".format(
138 self.label, var))
de9b991b
JR
139 raise Exception("Multiple definition of a special environment variable")
140 else:
141 env[var] = value
142
18aedaf9
JR
143 for var, (value, delimiter) in env_var.items():
144 tmp = [value]
145 if var in env:
146 tmp.append(env[var])
147 env[var] = delimiter.join(tmp)
148
de9b991b
JR
149 return env
150
151 def autobuild(self):
152 """
153 Perform the bootstrap, configuration, build and install the
154 project. Build dependencies if not already built
155 """
fe7b987e
JR
156 if (self.isConfigured and self.isBuilt and self.isInstalled):
157 return
158
159 if self._immutable:
160 raise Exception("Object is immutable. Illegal autobuild")
161
162 for key, dep in self.dependencies.items():
de9b991b
JR
163 dep.autobuild()
164
fe7b987e 165 if self.isConfigured ^ self.isBuilt ^ self.isInstalled:
de9b991b
JR
166 raise Exception("Project steps where manually triggered. Can't autobuild")
167
97d494b0
JR
168 _logger.debug("{} Autobuild configure".format(self.label))
169 try:
170 self.configure()
171 except subprocess.CalledProcessError as e:
172 _logger.error("{} Configure failed. See {} for more details.".format(self.label, self.log_path))
173 raise e
174
175 _logger.debug("{} Autobuild build".format(self.label))
176 try:
177 self.build()
178 except subprocess.CalledProcessError as e:
179 _logger.error("{} Build failed. See {} for more details.".format(self.label, self.log_path))
180 raise e
181
182 _logger.debug("{} Autobuild install".format(self.label))
183 try:
184 self.install()
185 except subprocess.CalledProcessError as e:
186 _logger.error("{} Install failed. See {} for more details.".format(self.label, self.log_path))
187 raise e
de9b991b
JR
188
189 def checkout(self):
fe7b987e
JR
190 if self._immutable:
191 raise Exception("Object is immutable. Illegal checkout")
192
de9b991b
JR
193 repo = git.Repo.clone_from(self.git_path, self.source_path)
194 commit = repo.commit(self.sha1)
195 repo.head.reference = commit
196 assert repo.head.is_detached
197 repo.head.reset(index=True, working_tree=True)
198
199 def bootstrap(self):
200 """
201 Bootstap the project. Raise subprocess.CalledProcessError on
202 bootstrap error.
203 """
fe7b987e
JR
204 if self._immutable:
205 raise Exception("Object is immutable. Illegal bootstrap")
206
207 out = os.path.join(self.log_path, "bootstrap.out")
208 err = os.path.join(self.log_path, "bootstrap.err")
209
de9b991b 210 os.chdir(self.source_path)
fe7b987e
JR
211 with open(out, 'w') as stdout, open(err, 'w') as stderr:
212 p = subprocess.run(['./bootstrap'], stdout=stdout, stderr=stderr)
de9b991b
JR
213 p.check_returncode()
214 return p
215
216 def configure(self):
217 """
218 Configure the project.
219 Raises subprocess.CalledProcessError on configure error
220 """
fe7b987e
JR
221 if self._immutable:
222 raise Exception("Object is immutable. Illegal configure")
223
de9b991b 224 # Check that all our dependencies were actually installed
fe7b987e 225 for key, dep in self.dependencies.items():
de9b991b
JR
226 if not dep.isInstalled:
227 # TODO: Custom exception here Dependency Error
228 raise Exception("Dependency project flagged as not installed")
229
fe7b987e
JR
230 out = os.path.join(self.log_path, "configure.out")
231 err = os.path.join(self.log_path, "configure.err")
5908717c
JR
232 env_file = os.path.join(self.log_path, "configure.env")
233
234 env = self.get_env()
235
236 with open(env_file, 'w') as tmp:
237 pprint.pprint(env, stream=tmp)
fe7b987e 238
de9b991b
JR
239 os.chdir(self.source_path)
240 args = ['./configure']
241 prefix = '--prefix={}'.format(self.installation_path)
242 args.append(prefix)
243 args.extend(self.custom_configure_flags)
244
245 # TODO: log output and add INFO log point
fe7b987e 246 with open(out, 'w') as stdout, open(err, 'w') as stderr:
5908717c 247 p = subprocess.run(args, env=env, stdout=stdout,
fe7b987e 248 stderr=stderr)
de9b991b
JR
249 p.check_returncode()
250 self.isConfigured = True
251 return p
252
253 def build(self):
254 """
255 Build the project. Raise subprocess.CalledProcessError on build
256 error.
257 """
fe7b987e
JR
258 if self._immutable:
259 raise Exception("Object is immutable. Illegal build")
260
261 out = os.path.join(self.log_path, "build.out")
262 err = os.path.join(self.log_path, "build.err")
263
de9b991b
JR
264 os.chdir(self.source_path)
265 args = ['make']
266 env = self.get_env()
de9b991b
JR
267
268 # Number of usable cpu
269 # https://docs.python.org/3/library/os.html#os.cpu_count
270 num_cpu = str(len(os.sched_getaffinity(0)))
271 args.append('-j')
272 args.append(num_cpu)
97d494b0 273 args.append('V=1')
de9b991b
JR
274
275 # TODO: log output and add INFO log point with args
fe7b987e
JR
276 with open(out, 'w') as stdout, open(err, 'w') as stderr:
277 p = subprocess.run(args, env=env, stdout=stdout,
278 stderr=stderr)
de9b991b
JR
279 p.check_returncode()
280 self.isBuilt = True
281 return p
282
283 def install(self):
284 """
285 Install the project. Raise subprocess.CalledProcessError on
286 bootstrap error
287 """
fe7b987e
JR
288 if self._immutable:
289 raise Exception("Object is immutable. Illegal install")
290
97d494b0
JR
291 out = os.path.join(self.log_path, "install.out")
292 err = os.path.join(self.log_path, "install.err")
fe7b987e 293
de9b991b
JR
294 os.chdir(self.source_path)
295 args = ['make', 'install']
296
297 # TODO: log output and add INFO log point
fe7b987e
JR
298 with open(out, 'w') as stdout, open(err, 'w') as stderr:
299 p = subprocess.run(args, env=self.get_env(), stdout=stdout,
300 stderr=stderr)
de9b991b
JR
301 p.check_returncode()
302 self.isInstalled = True
303 return p
304
305 def cleanup(self):
306 if os.path.exists(self.source_path):
307 shutil.rmtree(self.source_path)
308 if os.path.exists(self.installation_path):
309 shutil.rmtree(self.installation_path)
310
311
312class Lttng_modules(Project):
97d494b0
JR
313 def __init__(self, label, git_path, sha1, tmpdir):
314 super(Lttng_modules, self).__init__(label=label, git_path=git_path,
315 sha1=sha1, tmpdir=tmpdir)
f0acf3f3 316 self.add_special_env_variable("MODPROBE_OPTIONS","-d {}".format(self.installation_path))
97d494b0 317
de9b991b
JR
318 def bootstrap(self):
319 pass
320
321 def configure(self):
322 pass
323
324 def install(self):
fe7b987e
JR
325 if self._immutable:
326 raise Exception("Object is immutable. Illegal install")
de9b991b
JR
327 os.chdir(self.source_path)
328 args = ['make', 'INSTALL_MOD_PATH={}'.format(self.installation_path),
329 'modules_install']
330 p = subprocess.run(args, env=self.get_env(), stdout=subprocess.PIPE,
331 stderr=subprocess.PIPE)
332 p.check_returncode()
333
334 # Perform a local depmod
335 args = ['depmod', '-b', self.installation_path]
336 p = subprocess.run(args, env=self.get_env())
337 p.check_returncode()
338 self.isInstalled = True
339
2dd375bf
JR
340 def autobuild(self):
341 try:
342 super(Lttng_modules, self).autobuild()
343 except subprocess.CalledProcessError as e:
344 self.skip = True
345
de9b991b
JR
346
347class Lttng_ust(Project):
348 def __init__(self, label, git_path, sha1, tmpdir):
349 super(Lttng_ust, self).__init__(label=label, git_path=git_path,
350 sha1=sha1, tmpdir=tmpdir)
351 self.custom_configure_flags.extend(['--disable-man-pages'])
e5cbfcca
JR
352 self.custom_configure_flags.extend(['--enable-python-agent'])
353 self.custom_configure_flags.extend(['--enable-java-agent-jul'])
354
74eb7096
JR
355 jul_path = os.path.join(self.installation_path,
356 "share/java/liblttng-ust-agent.jar")
8e2c5f79 357 classpath = ":".join([jul_path, '.'])
74eb7096 358 self.add_special_env_variable("CLASSPATH", classpath)
de9b991b 359
2094d672
JR
360 def install(self):
361 super(Lttng_ust, self).install()
362 python_path = find_dir(self.installation_path, "lttngust")
363 if python_path:
364 # Fetch the parent of lttngust folder
365 python_path = os.path.dirname(python_path)
366 self.add_special_env_variable("PYTHONPATH", python_path)
367
368
de9b991b
JR
369
370class Lttng_tools(Project):
18aedaf9
JR
371 def __init__(self, label, git_path, sha1, tmpdir):
372 super(Lttng_tools, self).__init__(label=label, git_path=git_path,
373 sha1=sha1, tmpdir=tmpdir)
374 self.add_special_env_variable("LTTNG_SESSION_CONFIG_XSD_PATH",
375 os.path.join(self.installation_path, "share/xml/lttng/"))
de9b991b 376
816f8cc1
JR
377 # Find the mi xsd
378 for xsd in Settings.mi_xsd_file_name:
379 mi = find_file(self.source_path, xsd)
380 if mi:
381 break
382 if not mi:
383 raise Exception("MI xsd not found")
384 self.mi_xsd = mi
385
de9b991b
JR
386
387class Babeltrace(Project):
388 pass
This page took 0.040766 seconds and 5 git commands to generate.