-
Notifications
You must be signed in to change notification settings - Fork 1k
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
[bug] conan install --output-folder ignored for build context when cross-compiling #14418
Comments
Hi @DoDoENT I am trying to reproduce this, so far this is what I got: def test_install_output_folder():
c = TestClient()
profile = textwrap.dedent("""
[settings]
os=Windows
arch=x86_64
build_type=Release
[conf]
tools.cmake.cmake_layout:build_folder_vars=['settings.os', 'settings.arch']
""")
app = textwrap.dedent("""
from conan import ConanFile
from conan.tools.cmake import cmake_layout, CMakeDeps
class App(ConanFile):
settings = "os", "arch", "build_type"
requires = "lib/0.1"
tool_requires = "tool/0.1"
def layout(self):
cmake_layout(self)
def generate(self):
deps = CMakeDeps(self)
deps.build_context_activated = ["tool"]
deps.generate()
""")
c.save({"tool/conanfile.py": GenConanfile("tool", "0.1"),
"lib/conanfile.py": GenConanfile("lib", "0.1"),
"app/conanfile.py": app,
"profile": profile})
c.run("create tool")
c.run("create lib")
c.run("install app -of=my_output -pr=profile -pr:b=profile -s:b os=Linux -s:b arch=armv8")
print(c.out)
print(c.current_folder) But I can't see that build folder. It would be useful to have:
|
Similarly The from conan import ConanFile
from conan.tools.files import copy
import os
class NeuralNetworkModelsImageCaptureConan(ConanFile):
name = "ml_models_image_capture"
license = "EULA"
description = "ML models for Capture SDK"
url = "https://bitbucket.org/microblink/core-nn-models-image-capture"
python_requires = "conanfile_utils/[~0]@nenad/testing"
python_requires_extend = "conanfile_utils.MicroblinkConanFile"
def requirements(self):
self.requires('runner/[~1.0]@nenad/testing', transitive_headers=True, package_id_mode='full_package_mode')
@property
def _is_cross_compiling(self):
return self.settings.os not in ['Linux', 'Macos', 'Windows']
def build_requirements(self):
self.tool_requires('runner/<host_version>@nenad/testing')
if self._is_cross_compiling:
suffix = ''
if self.user and self.channel:
suffix = f'@{self.user}/{self.channel}'
self.tool_requires(f'ml_models_image_capture/{self.version}{suffix}')
def build(self):
pkg_build_args = {}
if self._is_cross_compiling:
pkg_build_args['MB_HOST_DUMP_FOLDER'] = os.path.join(
self.dependencies.direct_build['ml_models_image_capture'].cpp_info.resdirs[0],
'NeuralNetworkModel'
)
self.mb_build_target(build_only_vars=pkg_build_args)
def package(self):
self.mb_package_custom_libraries(libs=['DocumentCaptureModels'])
if self.settings.os == "Android":
copy(
self,
pattern="NeuralNetworkModel/*.rtttl",
src=self.build_folder,
dst=os.path.join(self.package_folder, "res")
)
else:
copy(
self,
pattern="NeuralNetworkModel/*.strop",
src=self.build_folder,
dst=os.path.join(self.package_folder, "res")
)
copy(
self,
pattern="*README.md",
src=self.source_folder,
dst=self.package_folder,
)
copy(
self,
pattern="NeuralNetworkModel/*.json",
src=self.source_folder,
dst=os.path.join(self.package_folder, "res"),
excludes=("*/info.json", "*/test_data.json"),
)
copy(
self,
pattern="NeuralNetworkModel/*.hpp",
src=self.build_folder,
dst=os.path.join(self.package_folder, "include"),
excludes=("*_ModelData.hpp", "*_Model.hpp"),
)
copy(
self,
pattern="model_names.cmake",
src=self.build_folder,
dst=self.package_folder,
)
def package_info(self):
self.cpp_info.libs = ["DocumentCaptureModels"]
self.cpp_info.resdirs = ['res']
# pylint: skip-file Maybe this is a side-effect of package tool-requiring itself? |
For sake of completness, here is also the from conan import ConanFile
from conan.tools.cmake import cmake_layout, CMake, CMakeDeps, CMakeToolchain
from conan.tools.env import VirtualRunEnv, VirtualBuildEnv
from conan.tools.files import copy, load
from conan.tools.build import can_run
from conan.tools.microsoft import VCVars
import os
from typing import List, AnyStr, Dict
class MicroblinkConanFile:
options = {
'log_level': ['Verbose', 'Debug', 'Info', 'WarningsAndErrors'],
'enable_timer': [True, False],
}
default_options = {
'log_level': 'WarningsAndErrors',
'enable_timer': False,
}
settings = "os", "compiler", "build_type", "arch"
no_copy_source = True
exports_sources = '*', '!test-data/*', '!.git/*', '!test_package/*'
package_type = 'static-library'
# -----------------------------------------------------------------------------
# Now follow the mb-specific methods
# -----------------------------------------------------------------------------
def mb_generate_with_cmake_args(self, cmake_args: Dict):
custom_cmake_cache_variables = 'user.microblink.cmaketoolchain:cache_variables'
vcvars = VCVars(self)
vcvars.generate()
self.mb_add_base_args(cmake_args)
tc = CMakeToolchain(self)
skip_user_presets = self.conf.get('user.microblink.cmaketoolchain:skip_user_presets', default=False)
if skip_user_presets:
tc.user_presets_path = False
for require, dependency in self.dependencies.items():
custom_cmake_options = dependency.conf_info.get(custom_cmake_cache_variables)
if custom_cmake_options is not None:
tc.cache_variables.update(custom_cmake_options)
# calculate variable with all build requirements paths, for purposes of
# mb_find_program, so that developers don't need to activate conanbuild environment,
# which is not easy in every IDE
buildenv_generator = VirtualBuildEnv(self)
paths = buildenv_generator.environment().vars(self).get('PATH', default='')
if self.settings.os == 'Windows':
tc.cache_variables['MB_CONAN_FIND_PATHS'] = paths
else:
tc.cache_variables['MB_CONAN_FIND_PATHS'] = paths.replace(':', ';')
direct_cmake_options = self.conf.get(custom_cmake_cache_variables)
if direct_cmake_options:
tc.cache_variables.update(direct_cmake_options)
tc.cache_variables.update(cmake_args)
tc.generate()
deps = CMakeDeps(self)
deps.generate()
if self.tested_reference_str is not None and can_run(self):
conanrun = VirtualRunEnv(self)
conanrun.generate(scope='run')
def mb_add_base_args(self, cmake_args: Dict):
if 'log_level' in self.options:
if self.options.log_level == 'Verbose':
cmake_log_level = 'LOG_VERBOSE'
elif self.options.log_level == 'Debug':
cmake_log_level = 'LOG_DEBUG'
elif self.options.log_level == 'Info':
cmake_log_level = 'LOG_INFO'
elif self.options.log_level == 'WarningsAndErrors':
cmake_log_level = 'LOG_WARNINGS_AND_ERRORS'
cmake_args['MB_GLOBAL_LOG_LEVEL'] = cmake_log_level
if 'enable_timer' in self.options:
cmake_args['MB_GLOBAL_ENABLE_TIMER'] = self.options.enable_timer
def mb_build_target(self, target: AnyStr = None, build_only_vars: Dict = {}):
cmake = CMake(self)
# invoked only when building the package in cache.
# We generally want to treat warnings as errors, but not during
# package build, as we want forward compatibilty with new compilers
# that add new warnings.
build_only_vars.update({
'MB_TREAT_WARNINGS_AS_ERRORS': 'OFF',
'MB_BUILDING_CONAN_PACKAGE': 'ON',
})
cmake.configure(
variables=build_only_vars
)
if self.settings.os == 'iOS':
if self.settings.os.sdk == 'maccatalyst':
# CMake currently does not support invoking Mac Catalyst builds
if target is None:
target = 'ALL_BUILD'
self.run(
f"xcodebuild build -configuration {self.settings.build_type} -scheme {target} " +
"-destination 'platform=macOS,variant=Mac Catalyst' ONLY_ACTIVE_ARCH=NO"
)
else:
cmake.build(target=target, build_tool_args=['-sdk', str(self.settings.os.sdk), 'ONLY_ACTIVE_ARCH=NO'])
else:
cmake.build(target=target)
def mb_cmake_install(self):
cmake = CMake(self)
if self.settings.os == 'iOS':
if self.settings.os.sdk == 'maccatalyst':
# CMake currently does not support invoking Mac Catalyst builds
self.run(
f"xcodebuild build -configuration {self.settings.build_type} -scheme install " +
"-destination 'platform=macOS,variant=Mac Catalyst' ONLY_ACTIVE_ARCH=NO"
)
else:
# cmake.install in conan v2 does not support build_tool_args, like cmake.build
self.run(
f"xcodebuild build -configuration {self.settings.build_type} -scheme install " +
f"-sdk {self.settings.os.sdk} ONLY_ACTIVE_ARCH=NO"
)
else:
cmake.install()
def mb_package_all_headers(self):
copy(
self,
pattern="*.h*",
src=os.path.join(self.source_folder, self.name, "Source"),
dst=os.path.join(self.package_folder, "include"),
keep_path=True
)
def mb_package_public_headers(self):
copy(
self,
pattern="*.h*",
src=os.path.join(self.source_folder, self.name, "Include"),
dst=os.path.join(self.package_folder, "include"),
keep_path=True
)
def mb_package_custom_libraries(self, libs: List[AnyStr], subfolders: List[AnyStr] = ['']):
if self.settings.os == 'Windows':
for lib in libs:
for subfolder in subfolders:
copy(
self,
pattern=f"{lib}.lib",
src=os.path.join(self.build_folder, subfolder),
dst=os.path.join(self.package_folder, 'lib'),
keep_path=False
)
copy(
self,
pattern=f"*{lib}.pdb",
src=os.path.join(self.build_folder, subfolder),
dst=os.path.join(self.package_folder, "lib"),
keep_path=False
)
elif self.settings.os in ['iOS', 'Macos']:
cmake_generator = self.conf.get('tools.cmake.cmaketoolchain:generator', default='Xcode')
if cmake_generator == 'Xcode':
prefix = str(self.settings.build_type)
if self.settings.os == 'iOS':
prefix = f"{self.settings.build_type}-{self.settings.os.sdk}"
for lib in libs:
for subfolder in subfolders:
copy(
self,
pattern=f"{prefix}/lib{lib}.a",
src=os.path.join(self.build_folder, subfolder),
dst=os.path.join(self.package_folder, "lib"),
keep_path=False
)
else:
for lib in libs:
for subfolder in subfolders:
copy(
self,
pattern=f"lib{lib}.a",
src=os.path.join(self.build_folder, subfolder),
dst=os.path.join(self.package_folder, "lib"),
keep_path=False
)
else:
for lib in libs:
for subfolder in subfolders:
copy(
self,
pattern=f"lib{lib}.a",
src=os.path.join(self.build_folder, subfolder),
dst=os.path.join(self.package_folder, "lib"),
keep_path=False
)
def mb_package_all_libraries(self, subfolders: List[AnyStr] = ['']):
self.mb_package_custom_libraries(['*'], subfolders)
def mb_define_apple_universal_binary(self):
if self.info.settings.os == 'Macos' or \
(self.info.settings.os == 'iOS' and self.info.settings.os.sdk in ['iphonesimulator', 'maccatalyst']):
cmake_generator = self.conf.get('tools.cmake.cmaketoolchain:generator', default='Xcode')
if cmake_generator == 'Xcode':
self.info.settings.arch = 'universal'
def mb_run_test(self, exename: AnyStr, args: List[AnyStr] = []):
if can_run(self):
suffix = ''
if self.settings.os == 'Windows':
suffix = '.exe'
bin_path = os.path.join(self.cpp.build.bindirs[0], exename + suffix)
if len(args) != 0:
bin_path += ' '
bin_path += ' '.join(args)
self.run(bin_path, env='conanrun')
def mb_define_editable_paths_for_components(self, components: List[AnyStr]):
for component_name in components:
self.cpp.build.components[component_name].libdirs = [
str(self.settings.build_type),
'lib',
f'lib/{self.settings.build_type}'
]
if self.settings.os == 'iOS':
self.cpp.build.components[component_name].libdirs.append(
f'{self.settings.build_type}-{self.settings.os.sdk}'
)
# add include to every subfolder that contains Include subfolder
self.cpp.source.components[component_name].includedirs = [
os.path.join(f.path, 'Include') for f in os.scandir(self.recipe_folder)
if f.is_dir() and os.path.exists(os.path.join(f.path, 'Include'))
]
# -----------------------------------------------------------------------------
# Now follow the default implementations of conan methods (no mb_ prefix)
# -----------------------------------------------------------------------------
def set_version(self):
"""Automatically parse version from README.md (if it exists)"""
readme_path = os.path.join(self.recipe_folder, 'README.md')
if self.version is None and os.path.exists(readme_path):
# load the version from README.md
readme = load(self, readme_path)
assert readme is not None
regex = r"^##\s+(\d+.\d+(.\d+(.\d+)?)?)+\s+$"
import re
version_match = re.search(regex, readme, re.MULTILINE)
assert version_match is not None
self.version = version_match.group(1)
def layout(self):
cmake_layout(self)
# support for editable packages
self.cpp.build.libdirs = [str(self.settings.build_type), 'lib', f'lib/{self.settings.build_type}']
if self.settings.os == 'iOS':
self.cpp.build.libdirs.append(f'{self.settings.build_type}-{self.settings.os.sdk}')
# add include to every subfolder that contains Include subfolder
self.cpp.source.includedirs = [
os.path.join(f.path, 'Include') for f in os.scandir(self.recipe_folder)
if f.is_dir() and os.path.exists(os.path.join(f.path, 'Include'))
]
def generate(self):
self.mb_generate_with_cmake_args({})
def build(self):
self.mb_build_target()
def package(self):
self.mb_cmake_install()
def package_id(self):
self.mb_define_apple_universal_binary()
class MicroblinkConanFilePackage(ConanFile):
name = "conanfile_utils"
version = "0.1.0"
package_type = 'python-require'
# pylint: skip-file |
You can imagine The |
Aha, maybe the |
Yes, I've confirmed it. The problem was due to It's a bit confusing that OK, so it's not a conan bug after all. Sorry for misreporting (I'm easy on a trigger lately 😛 ). |
Ok, that makes sense, if it is in editable mode, it is being consumed by others and that is generating that folder. I need to check anyway this, not sure if it would make sense to try to change it and align the editable with the consumer folders
No prob! 😄 Better reporting and then following up and closing that not giving feedback at all, or struggle too much trying to find some solution instead of asking. |
😄 , sometimes I even report a bug, then discover that it's not a bug and close it, just to discover that it was the bug after all, but a different one than initially thought (see here 😛 ) |
Checked in #14426 that |
Environment details
Steps to reproduce
Run
conan install . -pr emscripten --output-folder my-build-folder
I'd expect
my-build-folder
to be created with both subfolders foremscripten
andmacos
(build context).However, I end-up with
my-build-folder/emscripten
andbuild/macos
folders.And if there is additional
conan install . --output-folder my-build-folder
to also install for the host system,CMakeUserPresets.json
ends up with duplicate presets for mac (one inmy-build-folder
and one inbuild
folder).Logs
Example log with
ls
outputs from my run:The text was updated successfully, but these errors were encountered: