-
-
Notifications
You must be signed in to change notification settings - Fork 632
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
Conan (third party) support for ctypes native libraries #5998
Changes from 15 commits
19b2824
91641b7
5898ce8
cb79337
41067f8
70cb412
3ea6586
afb45c8
8bc0e29
1dab9c0
8631b9c
6a95c66
d6dde33
932a8af
bc50b16
bab73bb
bd8f792
abaef25
47994c4
67eae3f
579e8dd
7439e18
4c41611
a9cc38f
ac050f3
9bc4c35
b1ecc81
611579f
05c2cf7
40b60bb
1bf69da
ee0fa06
0047140
7fc20ac
529f58c
0818064
5803352
5fcc69b
abdffa6
e0e772a
8d028c0
cf64653
51d4829
b7b5916
4a8bedb
ea4c701
5dbaea4
84be507
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 |
---|---|---|
@@ -0,0 +1,91 @@ | ||
# coding=utf-8 | ||
# Copyright 2018 Pants project contributors (see CONTRIBUTORS.md). | ||
# Licensed under the Apache License, Version 2.0 (see LICENSE). | ||
|
||
from __future__ import (absolute_import, division, generators, nested_scopes, print_function, | ||
unicode_literals, with_statement) | ||
|
||
import logging | ||
import os | ||
|
||
from pex.interpreter import PythonInterpreter | ||
from pex.pex import PEX | ||
from pex.pex_builder import PEXBuilder | ||
from pex.pex_info import PexInfo | ||
|
||
from pants.backend.python.python_requirement import PythonRequirement | ||
from pants.backend.python.tasks.pex_build_util import dump_requirements | ||
from pants.backend.python.tasks.wrapped_pex import WrappedPEX | ||
from pants.base.build_environment import get_pants_cachedir | ||
from pants.subsystem.subsystem import Subsystem | ||
from pants.util.dirutil import safe_concurrent_creation | ||
|
||
|
||
log = logging.getLogger(__name__) | ||
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. This is always called 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. ✅ |
||
|
||
|
||
class Conan(Subsystem): | ||
"""Pex binary for the conan package manager.""" | ||
options_scope = 'conan' | ||
|
||
@staticmethod | ||
def default_conan_requirements(): | ||
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. I would make this a class field. 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. ✅ |
||
return ( | ||
'conan==1.4.4', | ||
'PyJWT>=1.4.0, <2.0.0', | ||
'requests>=2.7.0, <3.0.0', | ||
'colorama>=0.3.3, <0.4.0', | ||
'PyYAML>=3.11, <3.13.0', | ||
'patch==1.16', | ||
'fasteners>=0.14.1', | ||
'six>=1.10.0', | ||
'node-semver==0.2.0', | ||
'distro>=1.0.2, <1.2.0', | ||
'pylint>=1.8.1, <1.9.0', | ||
'future==0.16.0', | ||
'pygments>=2.0, <3.0', | ||
'astroid>=1.6, <1.7', | ||
'deprecation>=2.0, <2.1' | ||
) | ||
|
||
@classmethod | ||
def register_options(cls, register): | ||
super(Conan, cls).register_options(register) | ||
register('--conan-requirements', type=list, default=cls.default_conan_requirements(), | ||
advanced=True, help='The requirements used to build to conan client pex.') | ||
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. I generally prefer to have the help on a new line, but this isn't followed a lot and isn't that important. 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. Also, this should read 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. ✅ |
||
|
||
class ConanBinary(object): | ||
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. I would make this subclass 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. ✅ |
||
"""A `conan` PEX binary.""" | ||
|
||
def __init__(self, pex): | ||
self._pex = pex | ||
|
||
@property | ||
def pex(self): | ||
"""Return the conan binary PEX. | ||
|
||
:rtype: :class:`pex.pex.PEX` | ||
""" | ||
return self._pex | ||
|
||
@classmethod | ||
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. I usually try to move all overrides of Also, typically implementation versions have started at 0, not 1, but that's not important. 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. ✅ |
||
def implementation_version(cls): | ||
return super(Conan, cls).implementation_version() + [('Conan', 1)] | ||
|
||
def bootstrap_conan(self): | ||
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. This is neat - one day we should generalize it in a manner similar to JVMTools. 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. ✅ 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. Agreed, this is really neat! |
||
pex_info = PexInfo.default() | ||
pex_info.entry_point = 'conans.conan' | ||
conan_bootstrap_dir = os.path.join(get_pants_cachedir(), 'conan_support') | ||
conan_pex_path = os.path.join(conan_bootstrap_dir, 'conan_binary') | ||
interpreter = PythonInterpreter.get() | ||
if os.path.exists(conan_pex_path): | ||
conan_binary = WrappedPEX(PEX(conan_pex_path, interpreter), interpreter) | ||
return self.ConanBinary(conan_binary) | ||
else: | ||
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. This line has trailing whitespace. 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. ✅ |
||
with safe_concurrent_creation(conan_pex_path) as safe_path: | ||
builder = PEXBuilder(safe_path, interpreter, pex_info=pex_info) | ||
reqs = [PythonRequirement(req) for req in self.get_options().conan_requirements] | ||
dump_requirements(builder, interpreter, reqs, log) | ||
builder.freeze() | ||
conan_binary = WrappedPEX(PEX(conan_pex_path, interpreter), interpreter) | ||
return self.ConanBinary(conan_binary) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
# coding=utf-8 | ||
# Copyright 2018 Pants project contributors (see CONTRIBUTORS.md). | ||
# Licensed under the Apache License, Version 2.0 (see LICENSE). | ||
|
||
from __future__ import (absolute_import, division, generators, nested_scopes, print_function, | ||
unicode_literals, with_statement) | ||
|
||
from pants.base.payload import Payload | ||
from pants.base.payload_field import PrimitiveField | ||
from pants.base.validation import assert_list | ||
from pants.build_graph.target import Target | ||
|
||
|
||
class ExternalNativeLibrary(Target): | ||
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. This is a good name for this target at this point in time! It may be worth revisiting later, along with the C/C++ library target names. 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. ✅ |
||
"""A set of Conan package strings to be passed to the Conan package manager.""" | ||
|
||
@classmethod | ||
def alias(cls): | ||
return 'external_native_library' | ||
|
||
def __init__(self, payload=None, packages=None, **kwargs): | ||
""" | ||
:param packages: a list of Conan-style package strings | ||
|
||
Example: | ||
lzo/2.10@twitter/stable | ||
""" | ||
payload = payload or Payload() | ||
|
||
assert_list(packages, key_arg='packages') | ||
payload.add_fields({ | ||
'packages': PrimitiveField(packages), | ||
}) | ||
super(ExternalNativeLibrary, self).__init__(payload=payload, **kwargs) | ||
|
||
@property | ||
def packages(self): | ||
return self.payload.packages | ||
|
||
@property | ||
def lib_names(self): | ||
return [pkg_name.split('/')[0] for pkg_name in self.payload.packages] | ||
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. This should use 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. ✅ |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -11,6 +11,7 @@ | |
from pants.backend.native.subsystems.native_toolchain import NativeToolchain | ||
from pants.backend.native.targets.native_library import NativeLibrary | ||
from pants.backend.native.tasks.native_compile import NativeTargetDependencies, ObjectFiles | ||
from pants.backend.native.tasks.native_external_library_fetch import NativeExternalLibraryFetch | ||
from pants.backend.native.tasks.native_task import NativeTask | ||
from pants.base.exceptions import TaskError | ||
from pants.base.workunit import WorkUnit, WorkUnitLabel | ||
|
@@ -142,6 +143,16 @@ def _make_link_request(self, vt, compiled_objects_product, native_target_deps_pr | |
def _get_shared_lib_cmdline_args(self): | ||
return self.linker.platform.resolve_platform_specific(self._SHARED_CMDLINE_ARGS) | ||
|
||
def _get_third_party_lib_args(self): | ||
lib_args = [] | ||
tp_files_product = self.context.products.get_data(NativeExternalLibraryFetch.NativeExternalLibraryFiles) | ||
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. This product dependency needs to be required in a In general I really prefer to fetch all products in the 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. ✅ |
||
if tp_files_product.lib_names: | ||
for lib_name in tp_files_product.lib_names: | ||
lib_args.append('-l{}'.format(lib_name)) | ||
lib_dir_arg = '-L{}'.format(tp_files_product.lib) | ||
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. I think this logic (generating command-line arguments from 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. ✅ |
||
lib_args.append(lib_dir_arg) | ||
return lib_args | ||
|
||
def _execute_link_request(self, link_request): | ||
object_files = link_request.object_files | ||
|
||
|
@@ -156,6 +167,8 @@ def _execute_link_request(self, link_request): | |
# We are executing in the results_dir, so get absolute paths for everything. | ||
cmd = ([linker.exe_filename] + | ||
self._get_shared_lib_cmdline_args() + | ||
self._get_third_party_lib_args() + | ||
['-lstdc++'] + | ||
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. The |
||
['-o', os.path.abspath(resulting_shared_lib_path)] + | ||
[os.path.abspath(obj) for obj in object_files]) | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -11,6 +11,7 @@ | |
|
||
from pants.backend.native.config.environment import Executable | ||
from pants.backend.native.targets.native_library import NativeLibrary | ||
from pants.backend.native.tasks.native_external_library_fetch import NativeExternalLibraryFetch | ||
from pants.backend.native.tasks.native_task import NativeTask | ||
from pants.base.exceptions import TaskError | ||
from pants.base.workunit import WorkUnit, WorkUnitLabel | ||
|
@@ -55,6 +56,7 @@ class NativeCompile(NativeTask, AbstractClass): | |
# `NativeCompile` will use `workunit_label` as the name of the workunit when executing the | ||
# compiler process. `workunit_label` must be set to a string. | ||
workunit_label = None | ||
workunit_name = 'native-compile' | ||
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. Is this used anywhere? 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. Yup, on line 278 in the 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. Oh, that was a mistake, it's supposed to be 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 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. ✅ 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. Thanks! |
||
|
||
@classmethod | ||
def product_types(cls): | ||
|
@@ -185,10 +187,20 @@ def get_compiler(self): | |
def _compiler(self): | ||
return self.get_compiler() | ||
|
||
def get_third_party_include_dirs(self): | ||
inc_dir = [] | ||
tp_files_product = self.context.products.get_data(NativeExternalLibraryFetch.NativeExternalLibraryFiles) | ||
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. Again, it's usually much more clear to fetch products in the body of the 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. ✅ |
||
directory = tp_files_product.include | ||
if directory: | ||
inc_dir = [directory] | ||
return inc_dir | ||
|
||
def _make_compile_request(self, versioned_target, dependencies): | ||
target = versioned_target.target | ||
include_dirs = [self._include_dirs_for_target(dep_tgt) for dep_tgt in dependencies] | ||
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. Generally, if I'm mutating a variable, I isolate that along with the initialization of the variable and surround it by at least one line of whitespace on both sides, a habit I learned the hard way, and I would recommend doing that here and anywhere else this occurs. 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. ✅ |
||
include_dirs.extend(self.get_third_party_include_dirs()) | ||
sources_and_headers = self.get_sources_headers_for_target(target) | ||
|
||
return NativeCompileRequest( | ||
compiler=self._compiler, | ||
include_dirs=include_dirs, | ||
|
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.
We should have consistent formatting of commas, at least in the native backend. I'll use this style in the future so we don't have any conflict, but also, it's generally a very good idea to maintain the same style as the rest of the file/directory, no matter where it is. Style changes aren't usually big enough to require a change on their own, and many people may not point it out for fear of being nitpicky, and it's really helpful if you can make sure to uphold whatever style is used in the current file so people in the future editing this file are clear on which to use.