From 77c14525c39b9f1e020cd48827127d0a65deb59c Mon Sep 17 00:00:00 2001 From: Jussi Pakkanen Date: Sat, 20 Sep 2014 14:08:48 +0300 Subject: [PATCH] Autodetect runnable commands on unix (where chmod values might be wrong) and Windows (where files with weird suffixes are unrunnable. --- build.py | 2 +- dependencies.py | 50 ++++++++++++------- interpreter.py | 4 +- ninjabackend.py | 15 +++--- .../64 custom header generator/input.def | 1 + .../64 custom header generator/makeheader.py | 10 ++++ .../64 custom header generator/meson.build | 12 +++++ .../common/64 custom header generator/prog.c | 5 ++ 8 files changed, 71 insertions(+), 28 deletions(-) create mode 100644 test cases/common/64 custom header generator/input.def create mode 100644 test cases/common/64 custom header generator/makeheader.py create mode 100644 test cases/common/64 custom header generator/meson.build create mode 100644 test cases/common/64 custom header generator/prog.c diff --git a/build.py b/build.py index 3e9d79c34..eda2ac07d 100644 --- a/build.py +++ b/build.py @@ -710,7 +710,7 @@ class CustomTarget: if isinstance(c, str): final_cmd.append(c) elif isinstance(c, dependencies.ExternalProgram): - final_cmd.append(c.get_command()) + final_cmd += c.get_command() elif isinstance(c, BuildTarget) or isinstance(c, CustomTarget): self.dependencies.append(c) # GIR scanner will attempt to execute this binary but diff --git a/dependencies.py b/dependencies.py index 0f0e0e958..f11628ee2 100644 --- a/dependencies.py +++ b/dependencies.py @@ -25,6 +25,7 @@ import os, stat, glob, subprocess, shutil from coredata import MesonException import environment import mlog +from environment import is_windows class DependencyException(MesonException): def __init__(self, *args, **kwargs): @@ -131,21 +132,32 @@ class ExternalProgram(): def __init__(self, name, fullpath=None, silent=False, search_dir=None): self.name = name if fullpath is not None: - self.fullpath = fullpath + self.fullpath = [fullpath] else: - self.fullpath = shutil.which(name) - if self.fullpath is None and search_dir is not None: + self.fullpath = [shutil.which(name)] + if self.fullpath[0] is None and search_dir is not None: trial = os.path.join(search_dir, name) if os.access(trial, os.X_OK): - self.fullpath = trial + self.fullpath = [trial] + # Now getting desperate. Maybe it is a script file that is a) not chmodded + # executable or b) we are on windows so they can't be directly executed. + try: + first_line = open(trial).readline().strip() + if first_line.startswith('#!'): + commands = first_line[2:].split('#')[0].strip().split() + if environment.is_windows(): + commands[0] = commands[0].split('/')[-1] # Windows does not have /usr/bin. + self.fullpath = commands + [trial] + except Exception: + pass if not silent: if self.found(): - mlog.log('Program', mlog.bold(name), 'found:', mlog.green('YES'), '(%s)' % self.fullpath) + mlog.log('Program', mlog.bold(name), 'found:', mlog.green('YES'), '(%s)' % ' '.join(self.fullpath)) else: mlog.log('Program', mlog.bold(name), 'found:', mlog.red('NO')) def found(self): - return self.fullpath is not None + return self.fullpath[0] is not None def get_command(self): return self.fullpath @@ -445,7 +457,7 @@ class Qt5Dependency(Dependency): # Moc and rcc return a non-zero result when doing so. # What kind of an idiot thought that was a good idea? if self.moc.found(): - mp = subprocess.Popen([self.moc.get_command(), '-v'], + mp = subprocess.Popen(self.moc.get_command() + ['-v'], stdout=subprocess.PIPE, stderr=subprocess.PIPE) (stdout, stderr) = mp.communicate() stdout = stdout.decode().strip() @@ -458,13 +470,13 @@ class Qt5Dependency(Dependency): raise DependencyException('Moc preprocessor is not for Qt 5. Output:\n%s\n%s' % (stdout, stderr)) if not qt5toolinfo_printed: - mlog.log(' moc:', mlog.green('YES'), '(%s %s)' % \ - (self.moc.fullpath, moc_ver.split()[-1])) + mlog.log(' moc:', mlog.green('YES'), '(%s, %s)' % \ + (' '.join(self.moc.fullpath), moc_ver.split()[-1])) else: if not qt5toolinfo_printed: mlog.log(' moc:', mlog.red('NO')) if self.uic.found(): - up = subprocess.Popen([self.uic.get_command(), '-v'], + up = subprocess.Popen(self.uic.get_command() + ['-v'], stdout=subprocess.PIPE, stderr=subprocess.PIPE) (stdout, stderr) = up.communicate() stdout = stdout.decode().strip() @@ -477,13 +489,13 @@ class Qt5Dependency(Dependency): raise DependencyException('Uic compiler is not for Qt 5. Output:\n%s\n%s' % (stdout, stderr)) if not qt5toolinfo_printed: - mlog.log(' uic:', mlog.green('YES'), '(%s %s)' % \ - (self.uic.fullpath, uic_ver.split()[-1])) + mlog.log(' uic:', mlog.green('YES'), '(%s, %s)' % \ + (' '.join(self.uic.fullpath), uic_ver.split()[-1])) else: if not qt5toolinfo_printed: mlog.log(' uic:', mlog.red('NO')) if self.rcc.found(): - rp = subprocess.Popen([self.rcc.get_command(), '-v'], + rp = subprocess.Popen(self.rcc.get_command() + ['-v'], stdout=subprocess.PIPE, stderr=subprocess.PIPE) (stdout, stderr) = rp.communicate() stdout = stdout.decode().strip() @@ -496,8 +508,8 @@ class Qt5Dependency(Dependency): raise DependencyException('Rcc compiler is not for Qt 5. Output:\n%s\n%s' % (stdout, stderr)) if not qt5toolinfo_printed: - mlog.log(' rcc:', mlog.green('YES'), '(%s %s)'\ - % (self.rcc.fullpath, rcc_ver.split()[-1])) + mlog.log(' rcc:', mlog.green('YES'), '(%s, %s)'\ + % (' '.join(self.rcc.fullpath), rcc_ver.split()[-1])) else: if not qt5toolinfo_printed: mlog.log(' rcc:', mlog.red('NO')) @@ -534,16 +546,16 @@ class Qt5Dependency(Dependency): return True def get_generate_rules(self): - moc_rule = CustomRule([self.moc.get_command(), '$mocargs', '@INFILE@', '-o', '@OUTFILE@'], + moc_rule = CustomRule(self.moc.get_command() + ['$mocargs', '@INFILE@', '-o', '@OUTFILE@'], 'moc_@BASENAME@.cpp', 'moc_headers', 'moc_hdr_compile', 'Compiling header @INFILE@ with the moc preprocessor') - mocsrc_rule = CustomRule([self.moc.get_command(), '$mocargs', '@INFILE@', '-o', '@OUTFILE@'], + mocsrc_rule = CustomRule(self.moc.get_command() + ['$mocargs', '@INFILE@', '-o', '@OUTFILE@'], '@BASENAME@.moc', 'moc_sources', 'moc_src_compile', 'Compiling source @INFILE@ with the moc preprocessor') - ui_rule = CustomRule([self.uic.get_command(), '@INFILE@', '-o', '@OUTFILE@'], + ui_rule = CustomRule(self.uic.get_command() + ['@INFILE@', '-o', '@OUTFILE@'], 'ui_@BASENAME@.h', 'ui_files', 'ui_compile', 'Compiling @INFILE@ with the ui compiler') - rrc_rule = CustomRule([self.rcc.get_command(), '@INFILE@', '-o', '@OUTFILE@', + rrc_rule = CustomRule(self.rcc.get_command() + ['@INFILE@', '-o', '@OUTFILE@', '${rcc_args}'], '@BASENAME@.cpp','qresources', 'rc_compile', 'Compiling @INFILE@ with the rrc compiler') return [moc_rule, mocsrc_rule, ui_rule, rrc_rule] diff --git a/interpreter.py b/interpreter.py index 2d8980438..db1656821 100644 --- a/interpreter.py +++ b/interpreter.py @@ -811,7 +811,9 @@ class Interpreter(): for i in cargs: if not isinstance(i, str): raise InterpreterException('Run_command arguments must be strings.') - args = [cmd] + cargs + if not isinstance(cmd, list): + cmd = [cmd] + args = cmd + cargs in_builddir = kwargs.get('in_builddir', False) if not isinstance(in_builddir, bool): raise InterpreterException('in_builddir must be boolean.') diff --git a/ninjabackend.py b/ninjabackend.py index 2b2a155ad..616041ecc 100644 --- a/ninjabackend.py +++ b/ninjabackend.py @@ -200,8 +200,6 @@ class NinjaBackend(backends.Backend): if isinstance(gensource, build.CustomTarget): for src in gensource.output: src = os.path.join(gensource.subdir, src) - if self.environment.is_header(src): - header_deps.append(RawFilename(src)) if self.environment.is_source(src) and not self.environment.is_header(src): if is_unity: unity_deps.append(os.path.join(self.environment.get_build_dir(), RawFilename(src))) @@ -209,7 +207,10 @@ class NinjaBackend(backends.Backend): obj_list.append(self.generate_single_compile(target, outfile, RawFilename(src), True, header_deps)) else: - pass # perhaps print warning about the unknown file? + # Assume anything not specifically a source file is a header. This is because + # people generate files with weird suffixes (.inc, .fh) that they then include + # in their source files. + header_deps.append(RawFilename(src)) break # just to cut down on indentation size for src in gensource.get_outfilelist(): if self.environment.is_object(src): @@ -275,7 +276,7 @@ class NinjaBackend(backends.Backend): ofilenames = [os.path.join(target.subdir, i) for i in target.output] # FIXME, should not grab element at zero but rather expand all. deps = [os.path.join(i.get_subdir(), self.hackety_hack(i.get_filename())) for i in target.get_dependencies()] - srcs = [os.path.join(self.build_to_src, target.subdir, i) for i in target.sources] + srcs = [os.path.join(self.build_to_src, target.subdir, i) for i in target.sources] deps += srcs elem = NinjaBuildElement(ofilenames, 'CUSTOM_COMMAND', deps) cmd = [] @@ -976,9 +977,9 @@ rule FORTRAN_DEP_HACK infilelist = genlist.get_infilelist() outfilelist = genlist.get_outfilelist() if isinstance(exe, build.BuildTarget): - exe_file = os.path.join(self.environment.get_build_dir(), self.get_target_filename(exe)) + exe_arr = [os.path.join(self.environment.get_build_dir(), self.get_target_filename(exe))] else: - exe_file = exe.get_command() + exe_arr = exe.get_command() base_args = generator.get_arglist() for i in range(len(infilelist)): if len(generator.outputs) == 1: @@ -994,7 +995,7 @@ rule FORTRAN_DEP_HACK args = self.replace_outputs(args, self.get_target_private_dir(target), outfilelist) args = [x.replace("@SOURCE_DIR@", self.environment.get_source_dir()).replace("@BUILD_DIR@", self.get_target_private_dir(target)) for x in args] - cmdlist = [exe_file] + args + cmdlist = exe_arr + args elem = NinjaBuildElement(outfiles, 'CUSTOM_COMMAND', infilename) elem.add_item('DESC', 'Generating $out') if isinstance(exe, build.BuildTarget): diff --git a/test cases/common/64 custom header generator/input.def b/test cases/common/64 custom header generator/input.def new file mode 100644 index 000000000..573541ac9 --- /dev/null +++ b/test cases/common/64 custom header generator/input.def @@ -0,0 +1 @@ +0 diff --git a/test cases/common/64 custom header generator/makeheader.py b/test cases/common/64 custom header generator/makeheader.py new file mode 100644 index 000000000..5b175b5e0 --- /dev/null +++ b/test cases/common/64 custom header generator/makeheader.py @@ -0,0 +1,10 @@ +#!/usr/bin/python3 + +# NOTE: this file does not have the executable bit set. This tests that +# Meson can automatically parse shebang lines. + +import sys + +template = '#define RET_VAL %s\n' +output = template % (open(sys.argv[1]).readline().strip()) +open(sys.argv[2], 'w').write(output) diff --git a/test cases/common/64 custom header generator/meson.build b/test cases/common/64 custom header generator/meson.build new file mode 100644 index 000000000..3360f2329 --- /dev/null +++ b/test cases/common/64 custom header generator/meson.build @@ -0,0 +1,12 @@ +project('custom header generator', 'c') + +gen = find_program('makeheader.py') + +generated_h = custom_target('makeheader.py', +output : 'myheader.lh', # Suffix not .h to ensure this works with custom suffixes, too. +input : 'input.def', +command : [gen, '@INPUT0@', '@OUTPUT0@']) + +prog = executable('prog', 'prog.c', generated_h, +include_directories : include_directories('.')) +test('gentest', prog) diff --git a/test cases/common/64 custom header generator/prog.c b/test cases/common/64 custom header generator/prog.c new file mode 100644 index 000000000..184973a8a --- /dev/null +++ b/test cases/common/64 custom header generator/prog.c @@ -0,0 +1,5 @@ +#include"myheader.lh" + +int main(int argc, char **argv) { + return RET_VAL; +}