Skip to content
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

Merged
Show file tree
Hide file tree
Changes from 44 commits
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
19b2824
reimplement a previous PR -- ignore this
cosmicexplorer Apr 25, 2018
91641b7
Fix git history...v2
Jun 22, 2018
5898ce8
Merge with master
Jun 22, 2018
cb79337
Fix broken int test
Jun 22, 2018
41067f8
Add workaround for linux machines
Jun 22, 2018
70cb412
Add a conan requirement class, refactor conan subsystem, rename fetch…
Jun 22, 2018
3ea6586
Make use of PythonInterpreter.get() explicit
Jun 22, 2018
afb45c8
Refactor task product into its own class, allow multiple conan remote…
Jun 25, 2018
8bc0e29
Rename third party verbiage
Jun 25, 2018
1dab9c0
Add a thirdparty project that links a library file
Jun 26, 2018
8631b9c
Return linker settings to normal
Jun 26, 2018
6a95c66
Add __dso_handle workaround to attempt to pass CI
Jun 26, 2018
d6dde33
Add stdlib c++ to linker commandline for debugging
Jun 26, 2018
932a8af
Refactor names
Jun 26, 2018
bc50b16
Remove all usages of std lib in cpp examples to pass travis CI
Jun 26, 2018
bab73bb
Remove cruft
Jun 27, 2018
bd8f792
First round of cosmicexplorer's comments
Jun 27, 2018
abaef25
Merge branch 'master' of github.com:pantsbuild/pants into clivingston…
Jul 9, 2018
47994c4
Change name of c_library target to debug lint failure
Jul 9, 2018
67eae3f
Address cosmicexplorer's comments and apply patch for fixing include …
Jul 9, 2018
579e8dd
Fix integration test for osx by using clang++ instead of g++
Jul 10, 2018
7439e18
Fix integration test for osx by using clang++ instead of g++
Jul 10, 2018
4c41611
Remove cruft from tests and add clarifying comments
Jul 10, 2018
a9cc38f
Remove C code from 3rdparty testprojects
Jul 10, 2018
ac050f3
debug travis
Jul 10, 2018
9bc4c35
Revert regex refactor
Jul 10, 2018
b1ecc81
Debug patch for clang-based tests in CI
Jul 10, 2018
611579f
Travis debug 2
Jul 10, 2018
05c2cf7
Travis debug 3
Jul 10, 2018
40b60bb
Dummy change to refresh test sources in CI
Jul 10, 2018
1bf69da
just try
cosmicexplorer Jul 12, 2018
ee0fa06
Merge branch 'master' into native-thirdparty-passes-unit-tests
cosmicexplorer Jul 12, 2018
0047140
revert changes to llvm.py and test_native_toolchain.py
cosmicexplorer Jul 12, 2018
7fc20ac
add back g++ hack
cosmicexplorer Jul 12, 2018
529f58c
Change int test
Jul 13, 2018
0818064
Merge branch 'master' of github.com:pantsbuild/pants into clivingston…
Jul 13, 2018
5803352
Add cmdline slicing for conan calls due to new WrappedPex functionality
Jul 14, 2018
5fcc69b
Remove pdb statement
Jul 16, 2018
abdffa6
Merge branch 'master' of github.com:pantsbuild/pants into clivingston…
Jul 16, 2018
e0e772a
Dummy change to trigger CI
Jul 16, 2018
8d028c0
Fix osx test failure
Jul 16, 2018
cf64653
Merge branch 'master' of github.com:pantsbuild/pants into clivingston…
Jul 16, 2018
51d4829
Add github issue link to fixme in llvm.py
Jul 17, 2018
b7b5916
Add Danny's patch to fix broken unit tests
Jul 17, 2018
4a8bedb
Add github issue link and comment on why it makes sense to slice the …
Jul 17, 2018
ea4c701
Cosmicexplorer final comments
Jul 17, 2018
5dbaea4
Add more extensive testing
Jul 17, 2018
84be507
Add 1 issue link
Jul 18, 2018
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions src/python/pants/backend/native/register.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,13 @@
from pants.backend.native.subsystems.binaries.llvm import create_llvm_rules
from pants.backend.native.subsystems.native_toolchain import create_native_toolchain_rules
from pants.backend.native.subsystems.xcode_cli_tools import create_xcode_cli_tools_rules
from pants.backend.native.targets.external_native_library import ExternalNativeLibrary
from pants.backend.native.targets.native_artifact import NativeArtifact
from pants.backend.native.targets.native_library import CLibrary, CppLibrary
from pants.backend.native.tasks.c_compile import CCompile
from pants.backend.native.tasks.cpp_compile import CppCompile
from pants.backend.native.tasks.link_shared_libraries import LinkSharedLibraries
from pants.backend.native.tasks.native_external_library_fetch import NativeExternalLibraryFetch
from pants.build_graph.build_file_aliases import BuildFileAliases
from pants.goal.task_registrar import TaskRegistrar as task

Expand All @@ -24,6 +26,7 @@ def build_file_aliases():
targets={
CLibrary.alias(): CLibrary,
CppLibrary.alias(): CppLibrary,
ExternalNativeLibrary.alias(): ExternalNativeLibrary,
},
objects={
NativeArtifact.alias(): NativeArtifact,
Expand All @@ -34,6 +37,7 @@ def build_file_aliases():
def register_goals():
# FIXME(#5962): register these under the 'compile' goal when we eliminate the product transitive
# dependency from export -> compile.
task(name='native-third-party-fetch', action=NativeExternalLibraryFetch).install('native-compile')
task(name='c-for-ctypes', action=CCompile).install('native-compile')
task(name='cpp-for-ctypes', action=CppCompile).install('native-compile')
task(name='shared-libraries', action=LinkSharedLibraries).install('link')
Expand Down
25 changes: 20 additions & 5 deletions src/python/pants/backend/native/subsystems/binaries/llvm.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
from pants.engine.rules import RootRule, rule
from pants.engine.selectors import Select
from pants.util.dirutil import is_readable_dir
from pants.util.memo import memoized_method
from pants.util.memo import memoized_method, memoized_property


class LLVMReleaseUrlGenerator(BinaryToolUrlGenerator):
Expand Down Expand Up @@ -70,19 +70,34 @@ def linker(self, platform):
self._PLATFORM_SPECIFIC_LINKER_NAME),
library_dirs=[])

# FIXME: use ParseSearchDirs for this and other include directories -- we shouldn't be trying to
# guess the path here.
# https://github.com/pantsbuild/pants/issues/6143
@memoized_property
def _common_include_dirs(self):
return [os.path.join(self.select(), 'lib/clang', self.version(), 'include')]

@memoized_property
def _common_lib_dirs(self):
return [os.path.join(self.select(), 'lib')]

def c_compiler(self):
return CCompiler(
path_entries=self.path_entries(),
exe_filename='clang',
library_dirs=[],
include_dirs=[])
library_dirs=self._common_lib_dirs,
include_dirs=self._common_include_dirs)

@memoized_property
def _cpp_include_dirs(self):
return [os.path.join(self.select(), 'include/c++/v1')]

def cpp_compiler(self):
return CppCompiler(
path_entries=self.path_entries(),
exe_filename='clang++',
library_dirs=[],
include_dirs=[])
library_dirs=self._common_lib_dirs,
include_dirs=(self._cpp_include_dirs + self._common_include_dirs))


# FIXME(#5663): use this over the XCode linker!
Expand Down
78 changes: 78 additions & 0 deletions src/python/pants/backend/native/subsystems/conan.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
# 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, print_function, unicode_literals

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
from pants.util.objects import datatype


logger = logging.getLogger(__name__)


class Conan(Subsystem):
"""Pex binary for the conan package manager."""
options_scope = 'conan'
default_conan_requirements = (
'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 implementation_version(cls):
return super(Conan, cls).implementation_version() + [('Conan', 0)]

@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 the conan client pex.')

class ConanBinary(datatype(['pex'])):
"""A `conan` PEX binary."""
pass

def bootstrap_conan(self):
Copy link
Contributor

Choose a reason for hiding this comment

The 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.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor

Choose a reason for hiding this comment

The 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))
return self.ConanBinary(pex=conan_binary)
else:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This line has trailing whitespace.

Copy link
Contributor Author

Choose a reason for hiding this comment

The 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, logger)
builder.freeze()
conan_binary = WrappedPEX(PEX(conan_pex_path, interpreter))
return self.ConanBinary(pex=conan_binary)
59 changes: 38 additions & 21 deletions src/python/pants/backend/native/subsystems/native_toolchain.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,39 +71,44 @@ def select_linker(platform, native_toolchain):
# 'darwin': lambda: Get(Linker, XCodeCLITools, native_toolchain._xcode_cli_tools),
# 'linux': lambda: Get(Linker, Binutils, native_toolchain._binutils),
# })
#
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This whole file needs much better documentation. I will do that as part of #5951.

# NB: We need to link through a provided compiler's frontend, and we need to know where all the
# compiler's libraries/etc are, so we set the executable name to the C++ compiler, which can find
# its own set of C++-specific files for the linker if necessary. Using e.g. 'g++' as the linker
# appears to produce byte-identical output when linking even C-only object files, and also
# happens to work when C++ is used.
# Currently, OSX links through the clang++ frontend, and Linux links through the g++ frontend.
if platform.normalized_os_name == 'darwin':
# TODO(#5663): turn this into LLVM when lld works.
linker = yield Get(Linker, XCodeCLITools, native_toolchain._xcode_cli_tools)
llvm_c_compiler = yield Get(LLVMCCompiler, NativeToolchain, native_toolchain)
c_compiler = llvm_c_compiler.c_compiler
llvm_cpp_compiler = yield Get(LLVMCppCompiler, NativeToolchain, native_toolchain)
cpp_compiler = llvm_cpp_compiler.cpp_compiler
else:
linker = yield Get(Linker, Binutils, native_toolchain._binutils)
gcc_c_compiler = yield Get(GCCCCompiler, NativeToolchain, native_toolchain)
c_compiler = gcc_c_compiler.c_compiler
gcc_cpp_compiler = yield Get(GCCCppCompiler, NativeToolchain, native_toolchain)
cpp_compiler = gcc_cpp_compiler.cpp_compiler

libc_dirs = native_toolchain._libc_dev.get_libc_dirs(platform)

# NB: We need to link through a provided compiler's frontend, and we need to know where all the
# compiler's libraries/etc are, so we set the executable name to the C++ compiler, which can find
# its own set of C++-specific files for the linker if necessary. Using e.g. 'g++' as the linker
# appears to produce byte-identical output when linking even C-only object files, and also
# happens to work when C++ is used.
gcc_c_compiler = yield Get(GCCCCompiler, NativeToolchain, native_toolchain)
c_compiler = gcc_c_compiler.c_compiler
gcc_cpp_compiler = yield Get(GCCCppCompiler, NativeToolchain, native_toolchain)
cpp_compiler = gcc_cpp_compiler.cpp_compiler

# NB: If needing to create an environment for process invocation that could use either a compiler
# or a linker (e.g. when we compile native code from `python_dist()`s), use the environment from
# the linker object (in addition to any further customizations), which has the paths from the C
# and C++ compilers baked in.
# FIXME(#5951): we need a way to compose executables more hygienically.
linker = Linker(
path_entries=(
c_compiler.path_entries +
cpp_compiler.path_entries +
c_compiler.path_entries +
linker.path_entries),
exe_filename=cpp_compiler.exe_filename,
library_dirs=(
libc_dirs +
c_compiler.library_dirs +
cpp_compiler.library_dirs +
c_compiler.library_dirs +
linker.library_dirs))

yield linker
Expand Down Expand Up @@ -147,7 +152,7 @@ def select_llvm_cpp_compiler(platform, native_toolchain):
path_entries=(provided_clangpp.path_entries + xcode_clang.path_entries),
exe_filename=provided_clangpp.exe_filename,
library_dirs=(provided_clangpp.library_dirs + xcode_clang.library_dirs),
include_dirs=(xcode_clang.include_dirs + provided_clangpp.include_dirs))
include_dirs=(provided_clangpp.include_dirs + xcode_clang.include_dirs))
final_llvm_cpp_compiler = LLVMCppCompiler(clang_with_xcode_paths)
else:
gcc_cpp_compiler = yield Get(GCCCppCompiler, GCC, native_toolchain._gcc)
Expand Down Expand Up @@ -231,16 +236,28 @@ def select_gcc_cpp_compiler(platform, native_toolchain):
yield final_gcc_cpp_compiler


@rule(CCompiler, [Select(NativeToolchain)])
def select_c_compiler(native_toolchain):
llvm_c_compiler = yield Get(LLVMCCompiler, NativeToolchain, native_toolchain)
yield llvm_c_compiler.c_compiler
@rule(CCompiler, [Select(NativeToolchain), Select(Platform)])
def select_c_compiler(native_toolchain, platform):
if platform.normalized_os_name == 'darwin':
llvm_c_compiler = yield Get(LLVMCCompiler, NativeToolchain, native_toolchain)
c_compiler = llvm_c_compiler.c_compiler
else:
gcc_c_compiler = yield Get(GCCCCompiler, NativeToolchain, native_toolchain)
c_compiler = gcc_c_compiler.c_compiler

yield c_compiler


@rule(CppCompiler, [Select(NativeToolchain), Select(Platform)])
def select_cpp_compiler(native_toolchain, platform):
if platform.normalized_os_name == 'darwin':
llvm_cpp_compiler = yield Get(LLVMCppCompiler, NativeToolchain, native_toolchain)
cpp_compiler = llvm_cpp_compiler.cpp_compiler
else:
gcc_cpp_compiler = yield Get(GCCCppCompiler, NativeToolchain, native_toolchain)
cpp_compiler = gcc_cpp_compiler.cpp_compiler

@rule(CppCompiler, [Select(NativeToolchain)])
def select_cpp_compiler(native_toolchain):
llvm_cpp_compiler = yield Get(LLVMCppCompiler, NativeToolchain, native_toolchain)
yield llvm_cpp_compiler.cpp_compiler
yield cpp_compiler


def create_native_toolchain_rules():
Expand Down
43 changes: 43 additions & 0 deletions src/python/pants/backend/native/targets/external_native_library.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# 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, print_function, unicode_literals

import re

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):
Copy link
Contributor

Choose a reason for hiding this comment

The 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.

Copy link
Contributor Author

Choose a reason for hiding this comment

The 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 [re.match(r'^([^\/]+)\/', pkg_name).group(1) for pkg_name in self.payload.packages]
10 changes: 10 additions & 0 deletions src/python/pants/backend/native/tasks/cpp_compile.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,16 @@ def get_compile_settings(self):
def get_compiler(self):
return self._request_single(CppCompiler, self._toolchain)

def _make_compile_argv(self, compile_request):
# FIXME: this is a temporary fix, do not do any of this kind of introspection.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you please add a link to #5951 here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

prev_argv = super(CppCompile, self)._make_compile_argv(compile_request)

if compile_request.compiler.exe_filename == 'clang++':
new_argv = [prev_argv[0], '-nobuiltininc', '-nostdinc++'] + prev_argv[1:]
else:
new_argv = prev_argv
return new_argv

# FIXME(#5951): don't have any command-line args in the task or in the subsystem -- rather,
# subsystem options should be used to populate an `Executable` which produces its own arguments.
def extra_compile_args(self):
Expand Down
Loading