diff options
-rw-r--r-- | backends.py | 68 | ||||
-rw-r--r-- | build.py | 5 | ||||
-rw-r--r-- | coredata.py | 1 | ||||
-rw-r--r-- | dependencies.py | 3 | ||||
-rw-r--r-- | interpreter.py | 14 | ||||
-rw-r--r-- | ninjabackend.py | 64 | ||||
-rw-r--r-- | regen_checker.py | 42 | ||||
-rw-r--r-- | test cases/common/60 install script/myinstall.bat | 3 | ||||
-rw-r--r-- | test cases/linuxlike/2 external library/meson.build | 5 | ||||
-rw-r--r-- | vs2010backend.py | 301 |
10 files changed, 401 insertions, 105 deletions
diff --git a/backends.py b/backends.py index 2d1bc48..86cf6aa 100644 --- a/backends.py +++ b/backends.py @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -import os, pickle +import os, pickle, re import build import dependencies import mesonlib @@ -226,7 +226,7 @@ class Backend(): if isinstance(target, build.Executable): commands += dep.get_exe_args() - # Fortran rquires extra include directives. + # Fortran requires extra include directives. if compiler.language == 'fortran': for lt in target.link_targets: priv_dir = os.path.join(self.get_target_dir(lt), lt.get_basename() + lt.type_suffix()) @@ -334,3 +334,67 @@ class Backend(): mfobj['projects'] = self.build.dep_manifest open(ifilename, 'w').write(json.dumps(mfobj)) d.data.append([ifilename, ofilename]) + + def get_regen_filelist(self): + '''List of all files whose alteration means that the build + definition needs to be regenerated.''' + deps = [os.path.join(self.build_to_src, df) \ + for df in self.interpreter.get_build_def_files()] + if self.environment.is_cross_build(): + deps.append(os.path.join(self.build_to_src, + self.environment.coredata.cross_file)) + deps.append('meson-private/coredata.dat') + if os.path.exists(os.path.join(self.environment.get_source_dir(), 'meson_options.txt')): + deps.append(os.path.join(self.build_to_src, 'meson_options.txt')) + for sp in self.build.subprojects.keys(): + fname = os.path.join(self.environment.get_source_dir(), sp, 'meson_options.txt') + if os.path.isfile(fname): + deps.append(os.path.join(self.build_to_src, sp, 'meson_options.txt')) + return deps + + def eval_custom_target_command(self, target, absolute_paths=False): + ofilenames = [os.path.join(self.get_target_dir(target), i) for i in target.output] + srcs = [] + outdir = self.get_target_dir(target) + if absolute_paths: + outdir = os.path.join(self.environment.get_build_dir(), outdir) + for i in target.sources: + if isinstance(i, str): + fname = os.path.join(self.build_to_src, target.subdir, i) + else: + fname = i.rel_to_builddir(self.build_to_src) + if absolute_paths: + fname = os.path.join(self.environment.get_build_dir(), fname) + srcs.append(fname) + cmd = [] + for i in target.command: + if isinstance(i, build.CustomTarget): + # GIR scanner will attempt to execute this binary but + # it assumes that it is in path, so always give it a full path. + tmp = i.get_filename()[0] + i = os.path.join(self.get_target_dir(i), tmp) + for (j, src) in enumerate(srcs): + i = i.replace('@INPUT%d@' % j, src) + for (j, res) in enumerate(ofilenames): + i = i.replace('@OUTPUT%d@' % j, res) + if i == '@INPUT@': + cmd += srcs + elif i == '@OUTPUT@': + cmd += ofilenames + else: + if '@OUTDIR@' in i: + i = i.replace('@OUTDIR@', outdir) + elif '@PRIVATE_OUTDIR_' in i: + match = re.search('@PRIVATE_OUTDIR_(ABS_)?([-a-zA-Z0-9.@:]*)@', i) + source = match.group(0) + if match.group(1) is None and not absolute_paths: + lead_dir = '' + else: + lead_dir = self.environment.get_build_dir() + target_id = match.group(2) + i = i.replace(source, + os.path.join(lead_dir, + self.get_target_private_dir(self.build.targets[target_id]))) + cmd.append(i) + cmd = [i.replace('\\', '/') for i in cmd] + return (srcs, ofilenames, cmd) @@ -435,6 +435,8 @@ class BuildTarget(): return self.include_dirs def add_external_deps(self, deps): + if not isinstance(deps, list): + deps = [deps] for dep in deps: if hasattr(dep, 'held_object'): dep = dep.held_object @@ -443,6 +445,7 @@ class BuildTarget(): self.add_include_dirs(dep.include_directories) for l in dep.libraries: self.link(l) + self.add_external_deps(dep.ext_deps) elif isinstance(dep, dependencies.Dependency): self.external_deps.append(dep) self.process_sourcelist(dep.get_sources()) @@ -461,6 +464,8 @@ class BuildTarget(): if not isinstance(t, StaticLibrary) and \ not isinstance(t, SharedLibrary): raise InvalidArguments('Link target is not library.') + if self.is_cross != t.is_cross: + raise InvalidArguments('Tried to mix cross built and native libraries in target %s.' % self.name) self.link_targets.append(t) def set_generated(self, genlist): diff --git a/coredata.py b/coredata.py index fcfd745..b1a9919 100644 --- a/coredata.py +++ b/coredata.py @@ -137,6 +137,7 @@ class CoreData(): def __init__(self, options): self.guid = str(uuid.uuid4()).upper() self.test_guid = str(uuid.uuid4()).upper() + self.regen_guid = str(uuid.uuid4()).upper() self.target_guids = {} self.version = version self.builtin_options = {} diff --git a/dependencies.py b/dependencies.py index d3a82db..12d8e5e 100644 --- a/dependencies.py +++ b/dependencies.py @@ -59,11 +59,12 @@ class Dependency(): return False class InternalDependency(): - def __init__(self, incdirs, libraries, sources): + def __init__(self, incdirs, libraries, sources, ext_deps): super().__init__() self.include_directories = incdirs self.libraries = libraries self.sources = sources + self.ext_deps = ext_deps class PkgConfigDependency(Dependency): pkgconfig_found = None diff --git a/interpreter.py b/interpreter.py index 7d066f7..f71d7f5 100644 --- a/interpreter.py +++ b/interpreter.py @@ -1112,7 +1112,19 @@ class Interpreter(): if not isinstance(sources, list): sources = [sources] sources = self.source_strings_to_files(self.flatten(sources)) - dep = dependencies.InternalDependency(incs, libs, sources) + deps = kwargs.get('dependencies', []) + if not isinstance(deps, list): + deps = [deps] + final_deps = [] + for d in deps: + try: + d = d.held_object + except Exception: + pass + if not isinstance(d, (dependencies.Dependency, dependencies.ExternalLibrary)): + raise InterpreterException('Dependencies must be external deps') + final_deps.append(d) + dep = dependencies.InternalDependency(incs, libs, sources, final_deps) return InternalDependencyHolder(dep) @noKwargs diff --git a/ninjabackend.py b/ninjabackend.py index 350e195..9081736 100644 --- a/ninjabackend.py +++ b/ninjabackend.py @@ -21,7 +21,7 @@ from mesonlib import File from meson_install import InstallData from build import InvalidArguments from coredata import MesonException -import os, sys, shutil, pickle, re +import os, sys, pickle, re if mesonlib.is_windows(): quote_char = '"' @@ -236,7 +236,7 @@ class NinjaBackend(backends.Backend): obj_list.append(os.path.join(self.get_target_private_dir_abs(target), src)) elif not self.environment.is_header(src): if is_unity: - if '/' in src: + if self.has_dir_part(src): rel_src = src else: rel_src = os.path.join(self.get_target_private_dir_abs(target), src) @@ -287,19 +287,13 @@ class NinjaBackend(backends.Backend): self.generate_target(t, outfile) def generate_custom_target(self, target, outfile): - ofilenames = [os.path.join(self.get_target_dir(target), i) for i in target.output] + (srcs, ofilenames, cmd) = self.eval_custom_target_command(target) deps = [] for i in target.get_dependencies(): # FIXME, should not grab element at zero but rather expand all. if isinstance(i, list): i = i[0] deps.append(os.path.join(self.get_target_dir(i), i.get_filename()[0])) - srcs = [] - for i in target.sources: - if isinstance(i, str): - srcs.append(os.path.join(self.build_to_src, target.subdir, i)) - else: - srcs.append(i.rel_to_builddir(self.build_to_src)) if target.build_always: deps.append('PHONY') elem = NinjaBuildElement(ofilenames, 'CUSTOM_COMMAND', srcs) @@ -315,36 +309,6 @@ class NinjaBackend(backends.Backend): tmp = [tmp] for fname in tmp: elem.add_dep(os.path.join(self.get_target_dir(d), fname)) - cmd = [] - for i in target.command: - if isinstance(i, build.CustomTarget): - # GIR scanner will attempt to execute this binary but - # it assumes that it is in path, so always give it a full path. - tmp = i.get_filename()[0] - i = os.path.join(self.get_target_dir(i), tmp) - for (j, src) in enumerate(srcs): - i = i.replace('@INPUT%d@' % j, src) - for (j, res) in enumerate(ofilenames): - i = i.replace('@OUTPUT%d@' % j, res) - if i == '@INPUT@': - cmd += srcs - elif i == '@OUTPUT@': - cmd += ofilenames - else: - if '@OUTDIR@' in i: - i = i.replace('@OUTDIR@', self.get_target_dir(target)) - elif '@PRIVATE_OUTDIR_' in i: - match = re.search('@PRIVATE_OUTDIR_(ABS_)?([-a-zA-Z0-9.@:]*)@', i) - source = match.group(0) - if match.group(1) is None: - lead_dir = '' - else: - lead_dir = self.environment.get_build_dir() - target_id = match.group(2) - i = i.replace(source, - os.path.join(lead_dir, - self.get_target_dir(self.build.targets[target_id]))) - cmd.append(i) elem.add_item('COMMAND', cmd) elem.add_item('description', 'Generating %s with a custom command.' % target.name) @@ -1270,7 +1234,7 @@ rule FORTRAN_DEP_HACK if isinstance(src, RawFilename): rel_src = src.fname elif is_generated: - if '/' in src: + if self.has_dir_part(src): rel_src = src else: rel_src = os.path.join(self.get_target_private_dir_abs(target), src) @@ -1347,7 +1311,7 @@ rule FORTRAN_DEP_HACK for d in header_deps: if isinstance(d, RawFilename): d = d.fname - elif not '/' in d: + elif not self.has_dir_part(d): d = os.path.join(self.get_target_private_dir_abs(target), d) element.add_dep(d) for d in extra_deps: @@ -1355,7 +1319,7 @@ rule FORTRAN_DEP_HACK for d in order_deps: if isinstance(d, RawFilename): d = d.fname - elif not '/' in d : + elif not self.has_dir_part(d): d = os.path.join(self.get_target_private_dir_abs(target), d) element.add_orderdep(d) element.add_orderdep(pch_dep) @@ -1368,6 +1332,9 @@ rule FORTRAN_DEP_HACK self.check_outputs(element) return rel_obj + def has_dir_part(self, fname): + return '/' in fname or '\\' in fname + # Fortran is a bit weird (again). When you link against a library, just compiling a source file # requires the mod files that are output when single files are built. To do this right we would need to # scan all inputs and write out explicit deps for each file. That is stoo slow and too much effort so @@ -1610,18 +1577,7 @@ rule FORTRAN_DEP_HACK elem.write(outfile) self.check_outputs(elem) - deps = [os.path.join(self.build_to_src, df) \ - for df in self.interpreter.get_build_def_files()] - if self.environment.is_cross_build(): - deps.append(os.path.join(self.build_to_src, - self.environment.coredata.cross_file)) - deps.append('meson-private/coredata.dat') - if os.path.exists(os.path.join(self.environment.get_source_dir(), 'meson_options.txt')): - deps.append(os.path.join(self.build_to_src, 'meson_options.txt')) - for sp in self.build.subprojects.keys(): - fname = os.path.join(self.environment.get_source_dir(), sp, 'meson_options.txt') - if os.path.isfile(fname): - deps.append(os.path.join(self.build_to_src, sp, 'meson_options.txt')) + deps = self.get_regen_filelist() elem = NinjaBuildElement('build.ninja', 'REGENERATE_BUILD', deps) elem.add_item('pool', 'console') elem.write(outfile) diff --git a/regen_checker.py b/regen_checker.py new file mode 100644 index 0000000..a0fe028 --- /dev/null +++ b/regen_checker.py @@ -0,0 +1,42 @@ +#!/usr/bin/env python3 + +# Copyright 2015 The Meson development team + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at + +# http://www.apache.org/licenses/LICENSE-2.0 + +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import sys, os +import pickle, subprocess + +# This could also be used for XCode. + +def need_regen(regeninfo): + sln_time = os.stat(os.path.join(regeninfo.build_dir, regeninfo.solutionfile)).st_mtime + for i in regeninfo.depfiles: + curfile = os.path.join(regeninfo.build_dir, i) + curtime = os.stat(curfile).st_mtime + if curtime > sln_time: + return True + return False + +def regen(regeninfo): + scriptdir = os.path.split(__file__)[0] + mesonscript = os.path.join(scriptdir, 'meson.py') + cmd = [sys.executable, mesonscript, regeninfo.build_dir, regeninfo.source_dir, + '--backend=vs2010', 'secret-handshake'] + subprocess.check_call(cmd) + +if __name__ == '__main__': + regeninfo = pickle.load(open(os.path.join(sys.argv[1], 'regeninfo.dump'), 'rb')) + if need_regen(regeninfo): + regen(regeninfo) + sys.exit(0) diff --git a/test cases/common/60 install script/myinstall.bat b/test cases/common/60 install script/myinstall.bat new file mode 100644 index 0000000..7036077 --- /dev/null +++ b/test cases/common/60 install script/myinstall.bat @@ -0,0 +1,3 @@ +@ECHO OFF + +echo At this point we could do something. diff --git a/test cases/linuxlike/2 external library/meson.build b/test cases/linuxlike/2 external library/meson.build index 8be4daa..3e2c172 100644 --- a/test cases/linuxlike/2 external library/meson.build +++ b/test cases/linuxlike/2 external library/meson.build @@ -22,3 +22,8 @@ assert(not cc.links(nolinkcode, name : 'Failing link'), 'Linking succeeded when e = executable('zprog', 'prog.c', dependencies : zlib) test('libtest', e) + +# Test that ext deps work via an internal dep. +intdep = declare_dependency(dependencies : zlib) +exe2 = executable('zprog2', 'prog.c', dependencies : intdep) +test('libtest2', exe2) diff --git a/vs2010backend.py b/vs2010backend.py index 68e8e6c..7ea0150 100644 --- a/vs2010backend.py +++ b/vs2010backend.py @@ -1,4 +1,4 @@ -# Copyright 2014 The Meson development team +# Copyright 2014-2015 The Meson development team # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -13,11 +13,21 @@ # limitations under the License. import os, sys +import pickle import backends, build +import dependencies +import mlog import xml.etree.ElementTree as ET import xml.dom.minidom from coredata import MesonException +class RegenInfo(): + def __init__(self, source_dir, build_dir, depfiles, solutionfile): + self.source_dir = source_dir + self.build_dir = build_dir + self.depfiles = depfiles + self.solutionfile = solutionfile + class Vs2010Backend(backends.Backend): def __init__(self, build): super().__init__(build) @@ -29,35 +39,38 @@ class Vs2010Backend(backends.Backend): idgroup = ET.SubElement(parent_node, 'ItemDefinitionGroup') all_output_files = [] for genlist in target.get_generated_sources(): - generator = genlist.get_generator() - exe = generator.get_exe() - 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)) + if isinstance(genlist, build.CustomTarget): + all_output_files += [os.path.join(self.get_target_dir(genlist), i) for i in genlist.output] else: - exe_file = exe.get_command() - base_args = generator.get_arglist() - for i in range(len(infilelist)): - if len(infilelist) == len(outfilelist): - sole_output = os.path.join(self.get_target_private_dir_abs(target), outfilelist[i]) + generator = genlist.get_generator() + exe = generator.get_exe() + 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)) else: - sole_output = '' - curfile = infilelist[i] - infilename = os.path.join(self.environment.get_source_dir(), curfile) - outfiles = genlist.get_outputs_for(curfile) - outfiles = [os.path.join(self.get_target_private_dir_abs(target), of) for of in outfiles] - all_output_files += outfiles - args = [x.replace("@INPUT@", infilename).replace('@OUTPUT@', sole_output)\ - for x in base_args] - args = [x.replace("@SOURCE_DIR@", self.environment.get_source_dir()).replace("@BUILD_DIR@", self.get_target_private_dir_abs(target)) - for x in args] - fullcmd = [exe_file] + args - cbs = ET.SubElement(idgroup, 'CustomBuildStep') - ET.SubElement(cbs, 'Command').text = ' '.join(self.special_quote(fullcmd)) - ET.SubElement(cbs, 'Inputs').text = infilename - ET.SubElement(cbs, 'Outputs').text = ';'.join(outfiles) - ET.SubElement(cbs, 'Message').text = 'Generating sources from %s.' % infilename + exe_file = exe.get_command() + base_args = generator.get_arglist() + for i in range(len(infilelist)): + if len(infilelist) == len(outfilelist): + sole_output = os.path.join(self.get_target_private_dir_abs(target), outfilelist[i]) + else: + sole_output = '' + curfile = infilelist[i] + infilename = os.path.join(self.environment.get_source_dir(), curfile) + outfiles = genlist.get_outputs_for(curfile) + outfiles = [os.path.join(self.get_target_private_dir_abs(target), of) for of in outfiles] + all_output_files += outfiles + args = [x.replace("@INPUT@", infilename).replace('@OUTPUT@', sole_output)\ + for x in base_args] + args = [x.replace("@SOURCE_DIR@", self.environment.get_source_dir()).replace("@BUILD_DIR@", self.get_target_private_dir_abs(target)) + for x in args] + fullcmd = [exe_file] + args + cbs = ET.SubElement(idgroup, 'CustomBuildStep') + ET.SubElement(cbs, 'Command').text = ' '.join(self.special_quote(fullcmd)) + ET.SubElement(cbs, 'Inputs').text = infilename + ET.SubElement(cbs, 'Outputs').text = ';'.join(outfiles) + ET.SubElement(cbs, 'Message').text = 'Generating sources from %s.' % infilename pg = ET.SubElement(parent_node, 'PropertyGroup') ET.SubElement(pg, 'CustomBuildBeforeTargets').text = 'ClCompile' return all_output_files @@ -68,7 +81,21 @@ class Vs2010Backend(backends.Backend): sln_filename = os.path.join(self.environment.get_build_dir(), self.build.project_name + '.sln') projlist = self.generate_projects() self.gen_testproj('RUN_TESTS', os.path.join(self.environment.get_build_dir(), 'RUN_TESTS.vcxproj')) + self.gen_regenproj('REGEN', os.path.join(self.environment.get_build_dir(), 'REGEN.vcxproj')) self.generate_solution(sln_filename, projlist) + self.generate_regen_info(sln_filename) + open(os.path.join(self.environment.get_scratch_dir(), 'regen.stamp'), 'wb') + rulefile = os.path.join(self.environment.get_scratch_dir(), 'regen.rule') + if not os.path.exists(rulefile): + open(rulefile, 'w').write("# For some reason this needs to be here.") + + def generate_regen_info(self, sln_filename): + deps = self.get_regen_filelist() + regeninfo = RegenInfo(self.environment.get_source_dir(), + self.environment.get_build_dir(), + deps, + sln_filename) + pickle.dump(regeninfo, open(os.path.join(self.environment.get_scratch_dir(), 'regeninfo.dump'), 'wb')) def get_obj_target_deps(self, obj_list): result = {} @@ -77,6 +104,31 @@ class Vs2010Backend(backends.Backend): result[o.target.get_basename()] = True return result.keys() + def determine_deps(self, p): + all_deps = {} + target = self.build.targets[p[0]] + if isinstance(target, build.CustomTarget): + for d in target.dependencies: + all_deps[d.get_id()] = True + return all_deps + if isinstance(target, build.RunTarget): + for d in [target.command] + target.args: + if isinstance(d, build.BuildTarget): + all_deps[d.get_id()] = True + return all_deps + for ldep in target.link_targets: + all_deps[ldep.get_id()] = True + for objdep in self.get_obj_target_deps(target.objects): + all_deps[objdep] = True + for gendep in target.generated: + if isinstance(gendep, build.CustomTarget): + all_deps[gendep.get_id()] = True + else: + gen_exe = gendep.generator.get_exe() + if isinstance(gen_exe, build.Executable): + all_deps[gen_exe.get_id()] = True + return all_deps + def generate_solution(self, sln_filename, projlist): ofile = open(sln_filename, 'w') ofile.write('Microsoft Visual Studio Solution File, Format Version 11.00\n') @@ -85,34 +137,34 @@ class Vs2010Backend(backends.Backend): for p in projlist: prj_line = prj_templ % (self.environment.coredata.guid, p[0], p[1], p[2]) ofile.write(prj_line) - all_deps = {} - for ldep in self.build.targets[p[0]].link_targets: - all_deps[ldep.get_id()] = True - for objdep in self.get_obj_target_deps(self.build.targets[p[0]].objects): - all_deps[objdep] = True - for gendep in self.build.targets[p[0]].generated: - gen_exe = gendep.generator.get_exe() - if isinstance(gen_exe, build.Executable): - all_deps[gen_exe.get_id()] = True - if len(all_deps) > 0: - ofile.write('\tProjectSection(ProjectDependencies) = postProject\n') - for dep in all_deps.keys(): - guid = self.environment.coredata.target_guids[dep] - ofile.write('\t\t{%s} = {%s}\n' % (guid, guid)) - ofile.write('EndProjectSection\n') + all_deps = self.determine_deps(p) + ofile.write('\tProjectSection(ProjectDependencies) = postProject\n') + regen_guid = self.environment.coredata.regen_guid + ofile.write('\t\t{%s} = {%s}\n' % (regen_guid, regen_guid)) + for dep in all_deps.keys(): + guid = self.environment.coredata.target_guids[dep] + ofile.write('\t\t{%s} = {%s}\n' % (guid, guid)) + ofile.write('EndProjectSection\n') ofile.write('EndProject\n') test_line = prj_templ % (self.environment.coredata.guid, 'RUN_TESTS', 'RUN_TESTS.vcxproj', self.environment.coredata.test_guid) ofile.write(test_line) ofile.write('EndProject\n') + regen_line = prj_templ % (self.environment.coredata.guid, + 'REGEN', 'REGEN.vcxproj', self.environment.coredata.regen_guid) + ofile.write(regen_line) + ofile.write('EndProject\n') ofile.write('Global\n') ofile.write('\tGlobalSection(SolutionConfigurationPlatforms) = preSolution\n') ofile.write('\t\tDebug|Win32 = Debug|Win32\n') ofile.write('\tEndGlobalSection\n') ofile.write('\tGlobalSection(ProjectConfigurationPlatforms) = postSolution\n') + ofile.write('\t\t{%s}.Debug|Win32.ActiveCfg = Debug|Win32\n' % self.environment.coredata.regen_guid) + ofile.write('\t\t{%s}.Debug|Win32.Build.0 = Debug|Win32\n' % self.environment.coredata.regen_guid) for p in projlist: ofile.write('\t\t{%s}.Debug|Win32.ActiveCfg = Debug|Win32\n' % p[2]) - ofile.write('\t\t{%s}.Debug|Win32.Build.0 = Debug|Win32\n' % p[2]) + if not isinstance(self.build.targets[p[0]], build.RunTarget): + ofile.write('\t\t{%s}.Debug|Win32.Build.0 = Debug|Win32\n' % p[2]) ofile.write('\t\t{%s}.Debug|Win32.ActiveCfg = Debug|Win32\n' % self.environment.coredata.test_guid) ofile.write('\tEndGlobalSection\n') ofile.write('\tGlobalSection(SolutionProperties) = preSolution\n') @@ -161,10 +213,84 @@ class Vs2010Backend(backends.Backend): def special_quote(self, arr): return ['"%s"' % i for i in arr] + def create_basic_crap(self, target): + buildtype = self.environment.coredata.buildtype + platform = "Win32" + project_name = target.name + root = ET.Element('Project', {'DefaultTargets' : "Build", + 'ToolsVersion' : '4.0', + 'xmlns' : 'http://schemas.microsoft.com/developer/msbuild/2003'}) + confitems = ET.SubElement(root, 'ItemGroup', {'Label' : 'ProjectConfigurations'}) + prjconf = ET.SubElement(confitems, 'ProjectConfiguration', {'Include' : 'Debug|Win32'}) + p = ET.SubElement(prjconf, 'Configuration') + p.text= buildtype + pl = ET.SubElement(prjconf, 'Platform') + pl.text = platform + globalgroup = ET.SubElement(root, 'PropertyGroup', Label='Globals') + guidelem = ET.SubElement(globalgroup, 'ProjectGuid') + guidelem.text = self.environment.coredata.test_guid + kw = ET.SubElement(globalgroup, 'Keyword') + kw.text = 'Win32Proj' + p = ET.SubElement(globalgroup, 'Platform') + p.text= platform + pname= ET.SubElement(globalgroup, 'ProjectName') + pname.text = project_name + ET.SubElement(root, 'Import', Project='$(VCTargetsPath)\Microsoft.Cpp.Default.props') + type_config = ET.SubElement(root, 'PropertyGroup', Label='Configuration') + ET.SubElement(type_config, 'ConfigurationType') + ET.SubElement(type_config, 'CharacterSet').text = 'MultiByte' + ET.SubElement(type_config, 'UseOfMfc').text = 'false' + ET.SubElement(root, 'Import', Project='$(VCTargetsPath)\Microsoft.Cpp.props') + direlem = ET.SubElement(root, 'PropertyGroup') + fver = ET.SubElement(direlem, '_ProjectFileVersion') + fver.text = self.project_file_version + outdir = ET.SubElement(direlem, 'OutDir') + outdir.text = '.\\' + intdir = ET.SubElement(direlem, 'IntDir') + intdir.text = 'test-temp\\' + tname = ET.SubElement(direlem, 'TargetName') + tname.text = target.name + return root + + def gen_run_target_vcxproj(self, target, ofname, guid): + root = self.create_basic_crap(target) + action = ET.SubElement(root, 'ItemDefinitionGroup') + customstep = ET.SubElement(action, 'PostBuildEvent') + cmd_raw = [target.command] + target.args + cmd = [sys.executable, os.path.join(self.environment.get_script_dir(), 'commandrunner.py'), + self.environment.get_build_dir(), self.environment.get_source_dir(), + self.get_target_dir(target)] + for i in cmd_raw: + if isinstance(i, build.BuildTarget): + cmd.append(os.path.join(self.environment.get_build_dir(), self.get_target_filename(i))) + elif isinstance(i, dependencies.ExternalProgram): + cmd += i.fullpath + else: + cmd.append(i) + cmd_templ = '''"%s" '''*len(cmd) + ET.SubElement(customstep, 'Command').text = cmd_templ % tuple(cmd) + ET.SubElement(customstep, 'Message').text = 'Running custom command.' + ET.SubElement(root, 'Import', Project='$(VCTargetsPath)\Microsoft.Cpp.targets') + tree = ET.ElementTree(root) + tree.write(ofname, encoding='utf-8', xml_declaration=True) + def gen_custom_target_vcxproj(self, target, ofname, guid): - raise NotImplementedError('Custom target not implemented yet. Sorry.') + root = self.create_basic_crap(target) + action = ET.SubElement(root, 'ItemDefinitionGroup') + customstep = ET.SubElement(action, 'CustomBuildStep') + (srcs, ofilenames, cmd) = self.eval_custom_target_command(target, True) + cmd_templ = '''"%s" '''*len(cmd) + ET.SubElement(customstep, 'Command').text = cmd_templ % tuple(cmd) + ET.SubElement(customstep, 'Outputs').text = ';'.join([os.path.join(self.environment.get_build_dir(), i)\ + for i in ofilenames]) + ET.SubElement(customstep, 'Inputs').text = ';'.join([os.path.join(self.environment.get_build_dir(), i) \ + for i in srcs]) + ET.SubElement(root, 'Import', Project='$(VCTargetsPath)\Microsoft.Cpp.targets') + tree = ET.ElementTree(root) + tree.write(ofname, encoding='utf-8', xml_declaration=True) def gen_vcxproj(self, target, ofname, guid, compiler): + mlog.debug('Generating vcxproj %s.' % target.name) entrypoint = 'WinMainCRTStartup' subsystem = 'Windows' if isinstance(target, build.Executable): @@ -178,7 +304,9 @@ class Vs2010Backend(backends.Backend): conftype = 'DynamicLibrary' entrypoint = '_DllMainCrtStartup' elif isinstance(target, build.CustomTarget): - self.gen_custom_target_vcxproj(target, ofname, guid) + return self.gen_custom_target_vcxproj(target, ofname, guid) + elif isinstance(target, build.RunTarget): + return self.gen_run_target_vcxproj(target, ofname, guid) else: raise MesonException('Unknown target type for %s' % target.get_basename()) down = self.target_to_build_root(target) @@ -309,7 +437,10 @@ class Vs2010Backend(backends.Backend): relpath = h.rel_to_builddir(proj_to_src_root) ET.SubElement(inc_hdrs, 'CLInclude', Include=relpath) for h in gen_hdrs: - relpath = h.rel_to_builddir(proj_to_src_root) + if isinstance(h, str): + relpath = h + else: + relpath = h.rel_to_builddir(proj_to_src_root) ET.SubElement(inc_hdrs, 'CLInclude', Include = relpath) if len(sources) + len(gen_src) > 0: inc_src = ET.SubElement(root, 'ItemGroup') @@ -320,6 +451,10 @@ class Vs2010Backend(backends.Backend): relpath = self.relpath(s, target.subdir) ET.SubElement(inc_src, 'CLCompile', Include=relpath) ET.SubElement(root, 'Import', Project='$(VCTargetsPath)\Microsoft.Cpp.targets') + # Reference the regen target. + ig = ET.SubElement(root, 'ItemGroup') + pref = ET.SubElement(ig, 'ProjectReference', Include=os.path.join(self.environment.get_build_dir(), 'REGEN.vcxproj')) + ET.SubElement(pref, 'Project').text = self.environment.coredata.regen_guid tree = ET.ElementTree(root) tree.write(ofname, encoding='utf-8', xml_declaration=True) # ElementTree can not do prettyprinting so do it manually @@ -331,6 +466,78 @@ class Vs2010Backend(backends.Backend): txt = open(ofname, 'r').read() open(ofname, 'w').write(txt.replace('"', '"')) + def gen_regenproj(self, project_name, ofname): + buildtype = self.environment.coredata.buildtype + platform = "Win32" + root = ET.Element('Project', {'DefaultTargets': 'Build', + 'ToolsVersion' : '4.0', + 'xmlns' : 'http://schemas.microsoft.com/developer/msbuild/2003'}) + confitems = ET.SubElement(root, 'ItemGroup', {'Label' : 'ProjectConfigurations'}) + prjconf = ET.SubElement(confitems, 'ProjectConfiguration', {'Include' : 'Debug|Win32'}) + p = ET.SubElement(prjconf, 'Configuration') + p.text= buildtype + pl = ET.SubElement(prjconf, 'Platform') + pl.text = platform + globalgroup = ET.SubElement(root, 'PropertyGroup', Label='Globals') + guidelem = ET.SubElement(globalgroup, 'ProjectGuid') + guidelem.text = self.environment.coredata.test_guid + kw = ET.SubElement(globalgroup, 'Keyword') + kw.text = 'Win32Proj' + p = ET.SubElement(globalgroup, 'Platform') + p.text= platform + pname= ET.SubElement(globalgroup, 'ProjectName') + pname.text = project_name + ET.SubElement(root, 'Import', Project='$(VCTargetsPath)\Microsoft.Cpp.Default.props') + type_config = ET.SubElement(root, 'PropertyGroup', Label='Configuration') + ET.SubElement(type_config, 'ConfigurationType').text = "Utility" + ET.SubElement(type_config, 'CharacterSet').text = 'MultiByte' + ET.SubElement(type_config, 'UseOfMfc').text = 'false' + ET.SubElement(root, 'Import', Project='$(VCTargetsPath)\Microsoft.Cpp.props') + direlem = ET.SubElement(root, 'PropertyGroup') + fver = ET.SubElement(direlem, '_ProjectFileVersion') + fver.text = self.project_file_version + outdir = ET.SubElement(direlem, 'OutDir') + outdir.text = '.\\' + intdir = ET.SubElement(direlem, 'IntDir') + intdir.text = 'test-temp\\' + tname = ET.SubElement(direlem, 'TargetName') + tname.text = project_name + + action = ET.SubElement(root, 'ItemDefinitionGroup') + midl = ET.SubElement(action, 'Midl') + ET.SubElement(midl, "AdditionalIncludeDirectories").text = '%(AdditionalIncludeDirectories)' + ET.SubElement(midl, "OutputDirectory").text = '$(IntDir)' + ET.SubElement(midl, 'HeaderFileName').text = '%(Filename).h' + ET.SubElement(midl, 'TypeLibraryName').text = '%(Filename).tlb' + ET.SubElement(midl, 'InterfaceIdentifierFilename').text = '%(Filename)_i.c' + ET.SubElement(midl, 'ProxyFileName').text = '%(Filename)_p.c' + script_root = self.environment.get_script_dir() + regen_script = os.path.join(script_root, 'regen_checker.py') + private_dir = self.environment.get_scratch_dir() + cmd_templ = '''setlocal +"%s" "%s" "%s" +if %%errorlevel%% neq 0 goto :cmEnd +:cmEnd +endlocal & call :cmErrorLevel %%errorlevel%% & goto :cmDone +:cmErrorLevel +exit /b %%1 +:cmDone +if %%errorlevel%% neq 0 goto :VCEnd''' + igroup = ET.SubElement(root, 'ItemGroup') + custombuild = ET.SubElement(igroup, 'CustomBuild', Include='meson-private/regen.rule') + message = ET.SubElement(custombuild, 'Message') + message.text = 'Checking whether solution needs to be regenerated.' + ET.SubElement(custombuild, 'Command').text = cmd_templ % \ + (sys.executable, regen_script, private_dir) + ET.SubElement(custombuild, 'Outputs').text = os.path.join(self.environment.get_scratch_dir(), 'regen.stamp') + deps = self.get_regen_filelist() + depstr = ';'.join([os.path.join(self.environment.get_source_dir(), d) for d in deps]) + ET.SubElement(custombuild, 'AdditionalInputs').text = depstr + ET.SubElement(root, 'Import', Project='$(VCTargetsPath)\Microsoft.Cpp.targets') + ET.SubElement(root, 'ImportGroup', Label='ExtensionTargets') + tree = ET.ElementTree(root) + tree.write(ofname, encoding='utf-8', xml_declaration=True) + def gen_testproj(self, target_name, ofname): buildtype = self.environment.coredata.buildtype platform = "Win32" |