-
Notifications
You must be signed in to change notification settings - Fork 996
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
MSBuild: add argument to build() to opt-in autoconsumption of props files generated by MSBuildToolchain & MSBuildDeps #12817
Changes from 12 commits
7a60623
5326ef5
2976776
f6da520
d0fc429
86762f7
17d39c2
9ea12f8
8730bae
d3e48ba
24d75e5
a42e46f
f42f32c
3d178a6
68a2200
04e69cd
ab5295c
88cf4c1
0941ed3
b35577a
7b8e82a
b4e0801
97cc0fe
501e227
c4f5a86
c450449
7753e04
730cede
008c568
ce846a5
def7edc
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,9 @@ | ||
import os | ||
import xml.etree.ElementTree as ET | ||
|
||
from conan.tools.microsoft.msbuilddeps import MSBuildDeps | ||
from conan.tools.microsoft.toolchain import MSBuildToolchain | ||
from conan.tools.microsoft.visual import msbuild_arch | ||
from conans.errors import ConanException | ||
|
||
|
||
|
@@ -9,29 +15,22 @@ def msbuild_verbosity_cmd_line_arg(conanfile): | |
return '/verbosity:{}'.format(verbosity) | ||
|
||
|
||
def msbuild_arch(arch): | ||
return {'x86': 'x86', | ||
'x86_64': 'x64', | ||
'armv7': 'ARM', | ||
'armv8': 'ARM64'}.get(str(arch)) | ||
|
||
|
||
class MSBuild(object): | ||
def __init__(self, conanfile): | ||
self._conanfile = conanfile | ||
self.build_type = conanfile.settings.get_safe("build_type") | ||
self.configuration = conanfile.settings.get_safe("build_type") | ||
# if platforms: | ||
# msvc_arch.update(platforms) | ||
arch = conanfile.settings.get_safe("arch") | ||
msvc_arch = msbuild_arch(arch) | ||
if conanfile.settings.get_safe("os") == "WindowsCE": | ||
msvc_arch = conanfile.settings.get_safe("os.platform") | ||
self.platform = msvc_arch | ||
self.platform = msbuild_arch(conanfile) | ||
|
||
def command(self, sln, targets=None): | ||
def command(self, sln, targets=None, force_import_generated_files=False): | ||
cmd = ('msbuild "%s" /p:Configuration=%s /p:Platform=%s' | ||
% (sln, self.build_type, self.platform)) | ||
|
||
if force_import_generated_files: | ||
cmd += f" {self._force_import_generated_files_cmd_line_arg()}" | ||
|
||
verbosity = msbuild_verbosity_cmd_line_arg(self._conanfile) | ||
if verbosity: | ||
cmd += " {}".format(verbosity) | ||
|
@@ -48,11 +47,75 @@ def command(self, sln, targets=None): | |
|
||
return cmd | ||
|
||
def build(self, sln, targets=None): | ||
cmd = self.command(sln, targets=targets) | ||
def build(self, sln, targets=None, force_import_generated_files=False): | ||
cmd = self.command(sln, targets=targets, force_import_generated_files=force_import_generated_files) | ||
self._conanfile.run(cmd) | ||
|
||
@staticmethod | ||
def get_version(_): | ||
return NotImplementedError("get_version() method is not supported in MSBuild " | ||
"toolchain helper") | ||
|
||
def _get_concrete_props_file(self, root_props_file): | ||
concrete_props_file = "" | ||
|
||
root = ET.parse(root_props_file).getroot() | ||
importgroup_element = root.find("ImportGroup") | ||
if importgroup_element: | ||
expected_condition = f"'$(Configuration)' == '{self.configuration}' And '$(Platform)' == '{self.platform}'" | ||
for import_element in importgroup_element.iter("Import"): | ||
condition = import_element.attrib.get("Condition") | ||
if expected_condition == condition: | ||
concrete_props_file = import_element.attrib.get("Project") | ||
break | ||
|
||
if concrete_props_file: | ||
concrete_props_file = os.path.join(self._conanfile.generators_folder, concrete_props_file) | ||
|
||
if not concrete_props_file or not os.path.exists(concrete_props_file): | ||
raise ConanException( | ||
f"MSBuildToolchain props file is missing for configuration={self.configuration} and " | ||
f"platform={self.platform}. 'configuration' and 'platform' attributes must be the same " | ||
"respectively in MSBuildToolchain and MSBuild." | ||
) | ||
|
||
return concrete_props_file | ||
|
||
def _get_msbuildtoolchain_properties(self, root_props_file): | ||
properties = {} | ||
|
||
# Get properties from props file of configuration and platform | ||
concrete_props_file = self._get_concrete_props_file(root_props_file) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe this can be avoided with There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes maybe, I can try There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nop, it doesn't help :( |
||
root = ET.parse(concrete_props_file).getroot() | ||
for propertygroup in root.iter("PropertyGroup"): | ||
if propertygroup.attrib.get("Label") == "Configuration": | ||
for child in propertygroup: | ||
properties[child.tag] = child.text | ||
return properties | ||
|
||
def _force_import_generated_files_cmd_line_arg(self): | ||
cmd_args = [] | ||
props_paths = [] | ||
|
||
# MSBuildToolchan must be in generators for this MSBuild mode | ||
msbuildtoolchain_file = os.path.join(self._conanfile.generators_folder, MSBuildToolchain.filename) | ||
if not os.path.exists(msbuildtoolchain_file): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It is possible that some users want to inject only dependencies information, but they don't want to use the toolchain at all? Sounds very possible. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Il would make little sense. It would be like allowing |
||
raise ConanException("Missing MSBuildToolchain, it should be added to generators") | ||
props_paths.append(msbuildtoolchain_file) | ||
|
||
# Properties of MSBuildToolchain must be extracted and passed manually through command line | ||
# because they don't have precedence when props file is injected with /p:ForceImportBeforeCppTargets | ||
properties = self._get_msbuildtoolchain_properties(msbuildtoolchain_file) | ||
for k, v in properties.items(): | ||
cmd_args.append(f"/p:{k}=\"{v}\"") | ||
|
||
# MSBuildDeps generator is optional | ||
msbuilddeps_file = os.path.join(self._conanfile.generators_folder, MSBuildDeps.filename) | ||
if os.path.exists(msbuilddeps_file): | ||
props_paths.append(msbuilddeps_file) | ||
|
||
# Inject root props generated by MSBuildToolchain & MSBuildDeps | ||
if props_paths: | ||
cmd_args.append(f"/p:ForceImportBeforeCppTargets=\"{';'.join(props_paths)}\"") | ||
|
||
return " ".join(cmd_args) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am not sure why the specific file needs to be passed, if the general toolchain file already contains conditionals to pick the right configuration one.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Because it doesn't work.
I'm testing with several CCI recipes for the moment, and properties are ignored when they come only from props file (msbuild complains about PlatformToolset since it's not overridden, and when there is a hardcoded WholeProgramOptimization I can't disable it through a custom property in MSBuildToolchain). When I extract these values from toolchain file and inject them in command line, it works.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(obviously, I would be more than happy if a smart solution not involving to parse xml files could be found)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I still see a lot of complexity in this functionality.
Wouldn't it be way more straightforward to just inject the
<import>
section in thevcxproj
directly?