The Meson Build System
http://mesonbuild.com/
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
813 lines
41 KiB
813 lines
41 KiB
# Copyright 2014-2016 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 os, sys |
|
import pickle |
|
import re |
|
|
|
from mesonbuild import compilers |
|
from mesonbuild.build import BuildTarget |
|
from mesonbuild.mesonlib import File |
|
from . import backends |
|
from .. import build |
|
from .. import dependencies |
|
from .. import mlog |
|
import xml.etree.ElementTree as ET |
|
import xml.dom.minidom |
|
from ..mesonlib import MesonException |
|
from ..environment import Environment |
|
|
|
class RegenInfo(): |
|
def __init__(self, source_dir, build_dir, depfiles): |
|
self.source_dir = source_dir |
|
self.build_dir = build_dir |
|
self.depfiles = depfiles |
|
|
|
class Vs2010Backend(backends.Backend): |
|
def __init__(self, build): |
|
super().__init__(build) |
|
self.project_file_version = '10.0.30319.1' |
|
self.sources_conflicts = {} |
|
|
|
def object_filename_from_source(self, target, source): |
|
basename = os.path.basename(source.fname) |
|
filename_without_extension = '.'.join(basename.split('.')[:-1]) |
|
if basename in self.sources_conflicts[target.get_id()]: |
|
# If there are multiple source files with the same basename, we must resolve the conflict |
|
# by giving each a unique object output file. |
|
filename_without_extension = '.'.join(source.fname.split('.')[:-1]).replace('/', '_').replace('\\', '_') |
|
return filename_without_extension + '.' + self.environment.get_object_suffix() |
|
|
|
def resolve_source_conflicts(self): |
|
for name, target in self.build.targets.items(): |
|
if not isinstance(target, BuildTarget): |
|
continue |
|
conflicts = {} |
|
for s in target.get_sources(): |
|
if hasattr(s, 'held_object'): |
|
s = s.held_object |
|
if not isinstance(s, File): |
|
continue |
|
basename = os.path.basename(s.fname) |
|
conflicting_sources = conflicts.get(basename, None) |
|
if conflicting_sources is None: |
|
conflicting_sources = [] |
|
conflicts[basename] = conflicting_sources |
|
conflicting_sources.append(s) |
|
self.sources_conflicts[target.get_id()] = {name: src_conflicts for name, src_conflicts in conflicts.items() |
|
if len(src_conflicts) > 1} |
|
|
|
def generate_custom_generator_commands(self, target, parent_node): |
|
generator_output_files = [] |
|
commands = [] |
|
inputs = [] |
|
outputs = [] |
|
custom_target_include_dirs = [] |
|
custom_target_output_files = [] |
|
for genlist in target.get_generated_sources(): |
|
if isinstance(genlist, build.CustomTarget): |
|
custom_target_output_files += [os.path.join(self.get_target_dir(genlist), i) for i in genlist.output] |
|
idir = self.relpath(self.get_target_dir(genlist), self.get_target_dir(target)) |
|
if idir not in custom_target_include_dirs: |
|
custom_target_include_dirs.append(idir) |
|
else: |
|
generator = genlist.get_generator() |
|
exe = generator.get_exe() |
|
infilelist = genlist.get_infilelist() |
|
outfilelist = genlist.get_outfilelist() |
|
exe_arr = self.exe_object_to_cmd_array(exe) |
|
base_args = generator.get_arglist() |
|
target_private_dir = self.relpath(self.get_target_private_dir(target), self.get_target_dir(target)) |
|
for i in range(len(infilelist)): |
|
if len(infilelist) == len(outfilelist): |
|
sole_output = os.path.join(target_private_dir, outfilelist[i]) |
|
else: |
|
sole_output = '' |
|
curfile = infilelist[i] |
|
infilename = os.path.join(self.environment.get_source_dir(), curfile) |
|
outfiles_rel = genlist.get_outputs_for(curfile) |
|
outfiles = [os.path.join(target_private_dir, of) for of in outfiles_rel] |
|
generator_output_files += outfiles |
|
args = [x.replace("@INPUT@", infilename).replace('@OUTPUT@', sole_output)\ |
|
for x in base_args] |
|
args = self.replace_outputs(args, target_private_dir, outfiles_rel) |
|
args = [x.replace("@SOURCE_DIR@", self.environment.get_source_dir()).replace("@BUILD_DIR@", target_private_dir) |
|
for x in args] |
|
fullcmd = exe_arr + self.replace_extra_args(args, genlist) |
|
commands.append(' '.join(self.special_quote(fullcmd))) |
|
inputs.append(infilename) |
|
outputs.extend(outfiles) |
|
if len(commands) > 0: |
|
idgroup = ET.SubElement(parent_node, 'ItemDefinitionGroup') |
|
cbs = ET.SubElement(idgroup, 'CustomBuildStep') |
|
ET.SubElement(cbs, 'Command').text = '\r\n'.join(commands) |
|
ET.SubElement(cbs, 'Inputs').text = ";".join(inputs) |
|
ET.SubElement(cbs, 'Outputs').text = ';'.join(outputs) |
|
ET.SubElement(cbs, 'Message').text = 'Generating custom sources.' |
|
pg = ET.SubElement(parent_node, 'PropertyGroup') |
|
ET.SubElement(pg, 'CustomBuildBeforeTargets').text = 'ClCompile' |
|
return generator_output_files, custom_target_output_files, custom_target_include_dirs |
|
|
|
def generate(self, interp): |
|
self.resolve_source_conflicts() |
|
self.interpreter = interp |
|
self.platform = 'Win32' |
|
self.buildtype = self.environment.coredata.get_builtin_option('buildtype') |
|
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() |
|
Vs2010Backend.touch_regen_timestamp(self.environment.get_build_dir()) |
|
|
|
@staticmethod |
|
def get_regen_stampfile(build_dir): |
|
return os.path.join(os.path.join(build_dir, Environment.private_dir), 'regen.stamp') |
|
|
|
@staticmethod |
|
def touch_regen_timestamp(build_dir): |
|
open(Vs2010Backend.get_regen_stampfile(build_dir), 'w').close() |
|
|
|
def generate_regen_info(self): |
|
deps = self.get_regen_filelist() |
|
regeninfo = RegenInfo(self.environment.get_source_dir(), |
|
self.environment.get_build_dir(), |
|
deps) |
|
pickle.dump(regeninfo, open(os.path.join(self.environment.get_scratch_dir(), 'regeninfo.dump'), 'wb')) |
|
|
|
def get_obj_target_deps(self, obj_list): |
|
result = {} |
|
for o in obj_list: |
|
if isinstance(o, build.ExtractedObjects): |
|
result[o.target.get_id()] = 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.get_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') |
|
ofile.write('# Visual Studio 2010\n') |
|
prj_templ = prj_line = 'Project("{%s}") = "%s", "%s", "{%s}"\n' |
|
for p in projlist: |
|
prj_line = prj_templ % (self.environment.coredata.guid, p[0], p[1], p[2]) |
|
ofile.write(prj_line) |
|
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\t%s|%s = %s|%s\n' % (self.buildtype, self.platform, self.buildtype, self.platform)) |
|
ofile.write('\tEndGlobalSection\n') |
|
ofile.write('\tGlobalSection(ProjectConfigurationPlatforms) = postSolution\n') |
|
ofile.write('\t\t{%s}.%s|%s.ActiveCfg = %s|%s\n' % |
|
(self.environment.coredata.regen_guid, self.buildtype, self.platform, |
|
self.buildtype, self.platform)) |
|
ofile.write('\t\t{%s}.%s|%s.Build.0 = %s|%s\n' % |
|
(self.environment.coredata.regen_guid, self.buildtype, self.platform, |
|
self.buildtype, self.platform)) |
|
for p in projlist: |
|
ofile.write('\t\t{%s}.%s|%s.ActiveCfg = %s|%s\n' % |
|
(p[2], self.buildtype, self.platform, |
|
self.buildtype, self.platform)) |
|
if not isinstance(self.build.targets[p[0]], build.RunTarget): |
|
ofile.write('\t\t{%s}.%s|%s.Build.0 = %s|%s\n' % |
|
(p[2], self.buildtype, self.platform, |
|
self.buildtype, self.platform)) |
|
ofile.write('\t\t{%s}.%s|%s.ActiveCfg = %s|%s\n' % |
|
(self.environment.coredata.test_guid, self.buildtype, self.platform, |
|
self.buildtype, self.platform)) |
|
ofile.write('\tEndGlobalSection\n') |
|
ofile.write('\tGlobalSection(SolutionProperties) = preSolution\n') |
|
ofile.write('\t\tHideSolutionNode = FALSE\n') |
|
ofile.write('\tEndGlobalSection\n') |
|
ofile.write('EndGlobal\n') |
|
|
|
def generate_projects(self): |
|
projlist = [] |
|
comp = None |
|
for l, c in self.environment.coredata.compilers.items(): |
|
if l == 'c' or l == 'cpp': |
|
comp = c |
|
break |
|
if comp is None: |
|
raise RuntimeError('C and C++ compilers missing.') |
|
for name, target in self.build.targets.items(): |
|
outdir = os.path.join(self.environment.get_build_dir(), self.get_target_dir(target)) |
|
fname = name + '.vcxproj' |
|
relname = os.path.join(target.subdir, fname) |
|
projfile = os.path.join(outdir, fname) |
|
uuid = self.environment.coredata.target_guids[name] |
|
self.gen_vcxproj(target, projfile, uuid, comp) |
|
projlist.append((name, relname, uuid)) |
|
return projlist |
|
|
|
def split_sources(self, srclist): |
|
sources = [] |
|
headers = [] |
|
objects = [] |
|
languages = [] |
|
for i in srclist: |
|
if self.environment.is_header(i): |
|
headers.append(i) |
|
elif self.environment.is_object(i): |
|
objects.append(i) |
|
elif self.environment.is_source(i): |
|
sources.append(i) |
|
lang = self.lang_from_source_file(i) |
|
if lang not in languages: |
|
languages.append(lang) |
|
elif self.environment.is_library(i): |
|
pass |
|
else: |
|
# Everything that is not an object or source file is considered a header. |
|
headers.append(i) |
|
return (sources, headers, objects, languages) |
|
|
|
def target_to_build_root(self, target): |
|
if target.subdir == '': |
|
return '' |
|
|
|
directories = os.path.normpath(target.subdir).split(os.sep) |
|
return os.sep.join(['..']*len(directories)) |
|
|
|
def special_quote(self, arr): |
|
return ['"%s"' % i for i in arr] |
|
|
|
def create_basic_crap(self, target): |
|
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' : self.buildtype + '|' + self.platform}) |
|
p = ET.SubElement(prjconf, 'Configuration') |
|
p.text= self.buildtype |
|
pl = ET.SubElement(prjconf, 'Platform') |
|
pl.text = self.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 = self.platform + 'Proj' |
|
p = ET.SubElement(globalgroup, 'Platform') |
|
p.text= self.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 = target.get_id() + '\\' |
|
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): |
|
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(ofilenames) |
|
ET.SubElement(customstep, 'Inputs').text = ';'.join(srcs) |
|
ET.SubElement(root, 'Import', Project='$(VCTargetsPath)\Microsoft.Cpp.targets') |
|
tree = ET.ElementTree(root) |
|
tree.write(ofname, encoding='utf-8', xml_declaration=True) |
|
|
|
@classmethod |
|
def lang_from_source_file(cls, src): |
|
ext = src.split('.')[-1] |
|
if ext in compilers.c_suffixes: |
|
return 'c' |
|
if ext in compilers.cpp_suffixes: |
|
return 'cpp' |
|
raise MesonException('Could not guess language from source file %s.' % src) |
|
|
|
def add_pch(self, inc_cl, proj_to_src_dir, pch_sources, source_file): |
|
if len(pch_sources) <= 1: |
|
# We only need per file precompiled headers if we have more than 1 language. |
|
return |
|
lang = Vs2010Backend.lang_from_source_file(source_file) |
|
header = os.path.join(proj_to_src_dir, pch_sources[lang][0]) |
|
pch_file = ET.SubElement(inc_cl, 'PrecompiledHeaderFile') |
|
pch_file.text = header |
|
pch_include = ET.SubElement(inc_cl, 'ForcedIncludeFiles') |
|
pch_include.text = header + ';%(ForcedIncludeFiles)' |
|
pch_out = ET.SubElement(inc_cl, 'PrecompiledHeaderOutputFile') |
|
pch_out.text = '$(IntDir)$(TargetName)-%s.pch' % lang |
|
|
|
def add_additional_options(self, source_file, parent_node, extra_args, has_additional_options_set): |
|
if has_additional_options_set: |
|
# We only need per file options if they were not set per project. |
|
return |
|
lang = Vs2010Backend.lang_from_source_file(source_file) |
|
ET.SubElement(parent_node, "AdditionalOptions").text = ' '.join(extra_args[lang]) + ' %(AdditionalOptions)' |
|
|
|
@classmethod |
|
def quote_define_cmdline(cls, arg): |
|
return re.sub(r'^([-/])D(.*?)="(.*)"$', r'\1D\2=\"\3\"', arg) |
|
|
|
def gen_vcxproj(self, target, ofname, guid, compiler): |
|
mlog.debug('Generating vcxproj %s.' % target.name) |
|
entrypoint = 'WinMainCRTStartup' |
|
subsystem = 'Windows' |
|
if isinstance(target, build.Executable): |
|
conftype = 'Application' |
|
if not target.gui_app: |
|
subsystem = 'Console' |
|
entrypoint = 'mainCRTStartup' |
|
elif isinstance(target, build.StaticLibrary): |
|
conftype = 'StaticLibrary' |
|
elif isinstance(target, build.SharedLibrary): |
|
conftype = 'DynamicLibrary' |
|
entrypoint = '_DllMainCrtStartup' |
|
elif isinstance(target, build.CustomTarget): |
|
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) |
|
proj_to_src_root = os.path.join(down, self.build_to_src) |
|
proj_to_src_dir = os.path.join(proj_to_src_root, target.subdir) |
|
(sources, headers, objects, languages) = self.split_sources(target.sources) |
|
buildtype = self.buildtype |
|
project_name = target.name |
|
target_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' : self.buildtype + '|' + self.platform}) |
|
p = ET.SubElement(prjconf, 'Configuration') |
|
p.text= buildtype |
|
pl = ET.SubElement(prjconf, 'Platform') |
|
pl.text = self.platform |
|
globalgroup = ET.SubElement(root, 'PropertyGroup', Label='Globals') |
|
guidelem = ET.SubElement(globalgroup, 'ProjectGuid') |
|
guidelem.text = guid |
|
kw = ET.SubElement(globalgroup, 'Keyword') |
|
kw.text = self.platform + 'Proj' |
|
ns = ET.SubElement(globalgroup, 'RootNamespace') |
|
ns.text = target_name |
|
p = ET.SubElement(globalgroup, 'Platform') |
|
p.text= self.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 = conftype |
|
ET.SubElement(type_config, 'CharacterSet').text = 'MultiByte' |
|
ET.SubElement(type_config, 'WholeProgramOptimization').text = 'false' |
|
ET.SubElement(type_config, 'UseDebugLibraries').text = 'true' |
|
ET.SubElement(root, 'Import', Project='$(VCTargetsPath)\Microsoft.Cpp.props') |
|
generated_files, custom_target_output_files, generated_files_include_dirs = self.generate_custom_generator_commands(target, root) |
|
(gen_src, gen_hdrs, gen_objs, gen_langs) = self.split_sources(generated_files) |
|
(custom_src, custom_hdrs, custom_objs, custom_langs) = self.split_sources(custom_target_output_files) |
|
gen_src += custom_src |
|
gen_hdrs += custom_hdrs |
|
gen_langs += custom_langs |
|
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 = target.get_id() + '\\' |
|
tname = ET.SubElement(direlem, 'TargetName') |
|
tname.text = target_name |
|
inclinc = ET.SubElement(direlem, 'LinkIncremental') |
|
inclinc.text = 'true' |
|
|
|
compiles = ET.SubElement(root, 'ItemDefinitionGroup') |
|
clconf = ET.SubElement(compiles, 'ClCompile') |
|
opt = ET.SubElement(clconf, 'Optimization') |
|
opt.text = 'disabled' |
|
inc_dirs = ['.', self.relpath(self.get_target_private_dir(target), self.get_target_dir(target)), |
|
proj_to_src_dir] + generated_files_include_dirs |
|
|
|
extra_args = {'c': [], 'cpp': []} |
|
for l, args in self.environment.coredata.external_args.items(): |
|
if l in extra_args: |
|
extra_args[l] += args |
|
for l, args in self.build.global_args.items(): |
|
if l in extra_args: |
|
extra_args[l] += args |
|
for l, args in target.extra_args.items(): |
|
if l in extra_args: |
|
extra_args[l] += args |
|
general_args = compiler.get_buildtype_args(self.buildtype).copy() |
|
# FIXME all the internal flags of VS (optimization etc) are represented |
|
# by their own XML elements. In theory we should split all flags to those |
|
# that have an XML element and those that don't and serialise them |
|
# properly. This is a crapton of work for no real gain, so just dump them |
|
# here. |
|
general_args += compiler.get_option_compile_args(self.environment.coredata.compiler_options) |
|
for d in target.get_external_deps(): |
|
try: |
|
general_args += d.compile_args |
|
except AttributeError: |
|
pass |
|
|
|
for l, args in extra_args.items(): |
|
extra_args[l] = [Vs2010Backend.quote_define_cmdline(x) for x in args] |
|
|
|
languages += gen_langs |
|
has_language_specific_args = any(l != extra_args['c'] for l in extra_args.values()) |
|
additional_options_set = False |
|
if not has_language_specific_args or len(languages) == 1: |
|
if len(languages) == 0: |
|
extra_args = [] |
|
else: |
|
extra_args = extra_args[languages[0]] |
|
extra_args = general_args + extra_args |
|
if len(extra_args) > 0: |
|
extra_args.append('%(AdditionalOptions)') |
|
ET.SubElement(clconf, "AdditionalOptions").text = ' '.join(extra_args) |
|
additional_options_set = True |
|
|
|
for d in target.include_dirs: |
|
for i in d.incdirs: |
|
curdir = os.path.join(d.curdir, i) |
|
inc_dirs.append(self.relpath(curdir, target.subdir)) # build dir |
|
inc_dirs.append(os.path.join(proj_to_src_root, curdir)) # src dir |
|
for i in d.get_extra_build_dirs(): |
|
curdir = os.path.join(d.curdir, i) |
|
inc_dirs.append(self.relpath(curdir, target.subdir)) # build dir |
|
|
|
inc_dirs.append('%(AdditionalIncludeDirectories)') |
|
ET.SubElement(clconf, 'AdditionalIncludeDirectories').text = ';'.join(inc_dirs) |
|
preproc = ET.SubElement(clconf, 'PreprocessorDefinitions') |
|
rebuild = ET.SubElement(clconf, 'MinimalRebuild') |
|
rebuild.text = 'true' |
|
rtlib = ET.SubElement(clconf, 'RuntimeLibrary') |
|
rtlib.text = 'MultiThreadedDebugDLL' |
|
funclink = ET.SubElement(clconf, 'FunctionLevelLinking') |
|
funclink.text = 'true' |
|
pch_node = ET.SubElement(clconf, 'PrecompiledHeader') |
|
pch_sources = {} |
|
for lang in ['c', 'cpp']: |
|
pch = target.get_pch(lang) |
|
if len(pch) == 0: |
|
continue |
|
pch_node.text = 'Use' |
|
pch_sources[lang] = [pch[0], pch[1], lang] |
|
if len(pch_sources) == 1: |
|
# If there is only 1 language with precompiled headers, we can use it for the entire project, which |
|
# is cleaner than specifying it for each source file. |
|
pch_source = list(pch_sources.values())[0] |
|
header = os.path.join(proj_to_src_dir, pch_source[0]) |
|
pch_file = ET.SubElement(clconf, 'PrecompiledHeaderFile') |
|
pch_file.text = header |
|
pch_include = ET.SubElement(clconf, 'ForcedIncludeFiles') |
|
pch_include.text = header + ';%(ForcedIncludeFiles)' |
|
pch_out = ET.SubElement(clconf, 'PrecompiledHeaderOutputFile') |
|
pch_out.text = '$(IntDir)$(TargetName)-%s.pch' % pch_source[2] |
|
|
|
warnings = ET.SubElement(clconf, 'WarningLevel') |
|
warnings.text = 'Level3' |
|
debinfo = ET.SubElement(clconf, 'DebugInformationFormat') |
|
debinfo.text = 'EditAndContinue' |
|
resourcecompile = ET.SubElement(compiles, 'ResourceCompile') |
|
ET.SubElement(resourcecompile, 'PreprocessorDefinitions') |
|
link = ET.SubElement(compiles, 'Link') |
|
# Put all language args here, too. |
|
extra_link_args = compiler.get_option_link_args(self.environment.coredata.compiler_options) |
|
extra_link_args += compiler.get_buildtype_linker_args(self.buildtype) |
|
for l in self.environment.coredata.external_link_args.values(): |
|
for a in l: |
|
extra_link_args.append(a) |
|
for l in target.link_args: |
|
for a in l: |
|
extra_link_args.append(a) |
|
if len(extra_link_args) > 0: |
|
extra_link_args.append('%(AdditionalOptions)') |
|
ET.SubElement(link, "AdditionalOptions").text = ' '.join(extra_link_args) |
|
|
|
additional_links = [] |
|
for t in target.get_dependencies(): |
|
lobj = self.build.targets[t.get_id()] |
|
rel_path = self.relpath(lobj.subdir, target.subdir) |
|
linkname = os.path.join(rel_path, lobj.get_import_filename()) |
|
additional_links.append(linkname) |
|
for lib in self.get_custom_target_provided_libraries(target): |
|
additional_links.append(self.relpath(lib, self.get_target_dir(target))) |
|
additional_objects = [] |
|
for o in self.flatten_object_list(target, down): |
|
assert(isinstance(o, str)) |
|
additional_objects.append(o) |
|
for o in custom_objs: |
|
additional_objects.append(self.relpath(o, self.get_target_dir(target))) |
|
if len(additional_links) > 0: |
|
additional_links.append('%(AdditionalDependencies)') |
|
ET.SubElement(link, 'AdditionalDependencies').text = ';'.join(additional_links) |
|
ofile = ET.SubElement(link, 'OutputFile') |
|
ofile.text = '$(OutDir)%s' % target.get_filename() |
|
addlibdir = ET.SubElement(link, 'AdditionalLibraryDirectories') |
|
addlibdir.text = '%(AdditionalLibraryDirectories)' |
|
subsys = ET.SubElement(link, 'SubSystem') |
|
subsys.text = subsystem |
|
gendeb = ET.SubElement(link, 'GenerateDebugInformation') |
|
gendeb.text = 'true' |
|
if isinstance(target, build.SharedLibrary): |
|
ET.SubElement(link, 'ImportLibrary').text = target.get_import_filename() |
|
pdb = ET.SubElement(link, 'ProgramDataBaseFileName') |
|
pdb.text = '$(OutDir}%s.pdb' % target_name |
|
if isinstance(target, build.Executable): |
|
ET.SubElement(link, 'EntryPointSymbol').text = entrypoint |
|
targetmachine = ET.SubElement(link, 'TargetMachine') |
|
targetmachine.text = 'MachineX86' |
|
|
|
extra_files = target.extra_files |
|
if len(headers) + len(gen_hdrs) + len(extra_files) > 0: |
|
inc_hdrs = ET.SubElement(root, 'ItemGroup') |
|
for h in headers: |
|
relpath = h.rel_to_builddir(proj_to_src_root) |
|
ET.SubElement(inc_hdrs, 'CLInclude', Include=relpath) |
|
for h in gen_hdrs: |
|
ET.SubElement(inc_hdrs, 'CLInclude', Include=h) |
|
for h in target.extra_files: |
|
relpath = os.path.join(proj_to_src_dir, h) |
|
ET.SubElement(inc_hdrs, 'CLInclude', Include=relpath) |
|
|
|
if len(sources) + len(gen_src) + len(pch_sources) > 0: |
|
inc_src = ET.SubElement(root, 'ItemGroup') |
|
for s in sources: |
|
relpath = s.rel_to_builddir(proj_to_src_root) |
|
inc_cl = ET.SubElement(inc_src, 'CLCompile', Include=relpath) |
|
self.add_pch(inc_cl, proj_to_src_dir, pch_sources, s) |
|
self.add_additional_options(s, inc_cl, extra_args, additional_options_set) |
|
basename = os.path.basename(s.fname) |
|
if basename in self.sources_conflicts[target.get_id()]: |
|
ET.SubElement(inc_cl, 'ObjectFileName').text = "$(IntDir)" + self.object_filename_from_source(target, s) |
|
for s in gen_src: |
|
inc_cl = ET.SubElement(inc_src, 'CLCompile', Include=s) |
|
self.add_pch(inc_cl, proj_to_src_dir, pch_sources, s) |
|
self.add_additional_options(s, inc_cl, extra_args, additional_options_set) |
|
for lang in pch_sources: |
|
header, impl, suffix = pch_sources[lang] |
|
relpath = os.path.join(proj_to_src_dir, impl) |
|
inc_cl = ET.SubElement(inc_src, 'CLCompile', Include=relpath) |
|
pch = ET.SubElement(inc_cl, 'PrecompiledHeader') |
|
pch.text = 'Create' |
|
pch_out = ET.SubElement(inc_cl, 'PrecompiledHeaderOutputFile') |
|
pch_out.text = '$(IntDir)$(TargetName)-%s.pch' % suffix |
|
pch_file = ET.SubElement(inc_cl, 'PrecompiledHeaderFile') |
|
# MSBuild searches for the header relative from the implementation, so we have to use |
|
# just the file name instead of the relative path to the file. |
|
pch_file.text = os.path.split(header)[1] |
|
self.add_additional_options(impl, inc_cl, extra_args, additional_options_set) |
|
|
|
if len(objects) + len(additional_objects) > 0: |
|
# Do not add gen_objs to project file. Those are automatically used by MSBuild, because they are part of |
|
# the CustomBuildStep Outputs. |
|
inc_objs = ET.SubElement(root, 'ItemGroup') |
|
for s in objects: |
|
relpath = s.rel_to_builddir(proj_to_src_root) |
|
ET.SubElement(inc_objs, 'Object', Include=relpath) |
|
for s in additional_objects: |
|
ET.SubElement(inc_objs, 'Object', Include=s) |
|
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 |
|
doc = xml.dom.minidom.parse(ofname) |
|
open(ofname, 'w').write(doc.toprettyxml()) |
|
# World of horror! Python insists on not quoting quotes and |
|
# fixing the escaped " into &quot; whereas MSVS |
|
# requires quoted but not fixed elements. Enter horrible hack. |
|
txt = open(ofname, 'r').read() |
|
open(ofname, 'w').write(txt.replace('&quot;', '"')) |
|
|
|
def gen_regenproj(self, project_name, ofname): |
|
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' : self.buildtype + '|' + self.platform}) |
|
p = ET.SubElement(prjconf, 'Configuration') |
|
p.text= self.buildtype |
|
pl = ET.SubElement(prjconf, 'Platform') |
|
pl.text = self.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 = self.platform + 'Proj' |
|
p = ET.SubElement(globalgroup, 'Platform') |
|
p.text = self.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 = 'regen-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' |
|
regen_command = [sys.executable, |
|
self.environment.get_build_command(), |
|
'--internal', |
|
'regencheck'] |
|
private_dir = self.environment.get_scratch_dir() |
|
cmd_templ = '''setlocal |
|
"%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') |
|
rulefile = os.path.join(self.environment.get_scratch_dir(), 'regen.rule') |
|
if not os.path.exists(rulefile): |
|
with open(rulefile, 'w') as f: |
|
f.write("# Meson regen file.") |
|
custombuild = ET.SubElement(igroup, 'CustomBuild', Include=rulefile) |
|
message = ET.SubElement(custombuild, 'Message') |
|
message.text = 'Checking whether solution needs to be regenerated.' |
|
ET.SubElement(custombuild, 'Command').text = cmd_templ % \ |
|
('" "'.join(regen_command), private_dir) |
|
ET.SubElement(custombuild, 'Outputs').text = Vs2010Backend.get_regen_stampfile(self.environment.get_build_dir()) |
|
deps = self.get_regen_filelist() |
|
ET.SubElement(custombuild, 'AdditionalInputs').text = ';'.join(deps) |
|
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): |
|
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' : self.buildtype + '|' + self.platform}) |
|
p = ET.SubElement(prjconf, 'Configuration') |
|
p.text= self.buildtype |
|
pl = ET.SubElement(prjconf, 'Platform') |
|
pl.text = self.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 = self.platform + 'Proj' |
|
p = ET.SubElement(globalgroup, 'Platform') |
|
p.text= self.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 |
|
|
|
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' |
|
postbuild = ET.SubElement(action, 'PostBuildEvent') |
|
ET.SubElement(postbuild, 'Message') |
|
test_command = [sys.executable, |
|
self.environment.get_build_command(), |
|
'--internal', |
|
'test'] |
|
cmd_templ = '''setlocal |
|
"%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''' |
|
test_data = self.serialise_tests()[0] |
|
ET.SubElement(postbuild, 'Command').text =\ |
|
cmd_templ % ('" "'.join(test_command), test_data) |
|
ET.SubElement(root, 'Import', Project='$(VCTargetsPath)\Microsoft.Cpp.targets') |
|
tree = ET.ElementTree(root) |
|
tree.write(ofname, encoding='utf-8', xml_declaration=True) |
|
# ElementTree can not do prettyprinting so do it manually |
|
#doc = xml.dom.minidom.parse(ofname) |
|
#open(ofname, 'w').write(doc.toprettyxml())
|
|
|