diff options
Diffstat (limited to 'vs2010backend.py')
-rw-r--r-- | vs2010backend.py | 301 |
1 files changed, 254 insertions, 47 deletions
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" |