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

Overlinking detection is the headline here. Some CRAN changes for Windows / noarch too. #2635

Merged
merged 8 commits into from
Jan 19, 2018
8 changes: 4 additions & 4 deletions conda_build/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -294,20 +294,20 @@ def convert(package_file, output_dir=".", show_imports=False, platforms=None, fo
def test_installable(channel='defaults'):
"""Check to make sure that packages in channel are installable.
This is a consistency check for the channel."""
from .inspect import test_installable
from .inspect_pkg import test_installable
return test_installable(channel)


def inspect_linkages(packages, prefix=_sys.prefix, untracked=False, all_packages=False,
show_files=False, groupby='package', sysroot=''):
from .inspect import inspect_linkages
from .inspect_pkg import inspect_linkages
packages = _ensure_list(packages)
return inspect_linkages(packages, prefix=prefix, untracked=untracked, all_packages=all_packages,
show_files=show_files, groupby=groupby, sysroot=sysroot)


def inspect_objects(packages, prefix=_sys.prefix, groupby='filename'):
from .inspect import inspect_objects
from .inspect_pkg import inspect_objects
packages = _ensure_list(packages)
return inspect_objects(packages, prefix=prefix, groupby=groupby)

Expand All @@ -334,7 +334,7 @@ def inspect_hash_inputs(packages):
Returns a dictionary with a key for each input package and a value of the dictionary loaded
from the package's info/hash_input.json file
"""
from .inspect import get_hash_input
from .inspect_pkg import get_hash_input
return get_hash_input(packages)


Expand Down
2 changes: 1 addition & 1 deletion conda_build/build.py
Original file line number Diff line number Diff line change
Expand Up @@ -710,7 +710,7 @@ def post_process_files(m, initial_prefix_files):
sys.exit(indent("""Error: Untracked file(s) %s found in conda-meta directory.
This error usually comes from using conda in the build script. Avoid doing this, as it
can lead to packages that include their dependencies.""" % meta_files))
post_build(m, new_files, build_python=python, config=m.config)
post_build(m, new_files, build_python=python)

entry_point_script_names = get_entry_point_script_names(m.get_value('build/entry_points'))
if m.noarch == 'python':
Expand Down
5 changes: 5 additions & 0 deletions conda_build/cli/main_build.py
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,11 @@ def parse_args(args):
" you package may depend on files that are not included in the package, and may pass"
"tests, but ultimately fail on installed systems.")
)
p.add_argument(
"--no-error-overlinking", dest='error_overlinking', default=True, action="store_false",
help=("Disable error when shared libraries from transitive dependencies are directly"
"linked to any executables or shared libraries in built packages.")
)
p.add_argument(
"--long-test-prefix", default=True, action="store_false",
help=("Use a long prefix for the test prefix, as well as the build prefix. Affects only "
Expand Down
1 change: 1 addition & 0 deletions conda_build/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ def _ensure_dir(path):
Setting('_src_cache_root', abspath(expanduser(expandvars(
cc_conda_build.get('cache_dir')))) if cc_conda_build.get('cache_dir') else None),
Setting('copy_test_source_files', True),
Setting('error_overlinking', True),

Setting('index', None),
# support legacy recipes where only build is specified and expected to be the
Expand Down
11 changes: 5 additions & 6 deletions conda_build/inspect.py → conda_build/inspect_pkg.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,17 +15,16 @@
import sys
import tempfile

from .conda_interface import (iteritems, specs_from_args, is_linked, linked_data, linked,
get_index)
from .conda_interface import display_actions, install_actions
from .conda_interface import memoized


from conda_build.os_utils.ldd import get_linkages, get_package_obj_files, get_untracked_obj_files
from conda_build.os_utils.macho import get_rpaths, human_filetype
from conda_build.utils import (groupby, getter, comma_join, rm_rf, package_has_file, get_logger,
ensure_list)

from conda_build.conda_interface import (iteritems, specs_from_args, is_linked, linked_data, linked,
get_index)
from conda_build.conda_interface import display_actions, install_actions
from conda_build.conda_interface import memoized


@memoized
def dist_files(prefix, dist):
Expand Down
41 changes: 25 additions & 16 deletions conda_build/os_utils/ldd.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@
from conda_build.conda_interface import untracked
from conda_build.conda_interface import linked_data

from conda_build import post
from conda_build.os_utils.macho import otool
from conda_build.os_utils.pyldd import inspect_linkages
from conda_build.os_utils.pyldd import codefile_class, inspect_linkages, machofile, is_codefile


LDD_RE = re.compile(r'\s*(.*?)\s*=>\s*(.*?)\s*\(.*\)')
LDD_NOT_FOUND_RE = re.compile(r'\s*(.*?)\s*=>\s*not found')
Expand Down Expand Up @@ -49,6 +49,14 @@ def get_linkages(obj_files, prefix, sysroot):
path = join(prefix, f)
# ldd quite often fails on foreign architectures.
ldd_failed = False
# Detect the filetype to emulate what the system-native tool does.
klass = codefile_class(path)
if klass == machofile:
resolve_filenames = False
recurse = False
else:
resolve_filenames = True
recurse = True
try:
if sys.platform.startswith('linux'):
res[f] = ldd(path)
Expand All @@ -58,21 +66,22 @@ def get_linkages(obj_files, prefix, sysroot):
except:
ldd_failed = True
finally:
res_py = inspect_linkages(path, sysroot=sysroot)
res_py = inspect_linkages(path, resolve_filenames=resolve_filenames,
sysroot=sysroot, recurse=recurse)
res_py = [(basename(lp), lp) for lp in res_py]
# print("set(res_py) {}".format(set(res_py)))
if ldd_failed:
res[f] = res_py
# else:
# print("set(res[f]) = {}".format(set(res[f])))
# if set(res[f]) != set(res_py):
# print("WARNING: pyldd disagrees with ldd/otool. This will not cause any")
# print("WARNING: problems for this build, but please file a bug at:")
# print("WARNING: https://github.com/conda/conda-build")
# print("WARNING: and (if possible) attach file {}".format(path))
# print("WARNING: ldd/tool gives {}, pyldd gives {}"
# .format(set(res[f]), set(res_py)))

else:
if set(res[f]) != set(res_py):
print("WARNING: pyldd disagrees with ldd/otool. This will not cause any")
print("WARNING: problems for this build, but please file a bug at:")
print("WARNING: https://github.com/conda/conda-build")
print("WARNING: and (if possible) attach file {}".format(path))
print("WARNING: \nldd/otool gives:\n{}\npyldd gives:\n{}\n"
.format("\n".join(str(e) for e in res[f]), "\n".join(str(e)
for e in res_py)))
print("Diffs\n{}".format(set(res[f]) - set(res_py)))
print("Diffs\n{}".format(set(res_py) - set(res[f])))
return res


Expand All @@ -84,7 +93,7 @@ def get_package_obj_files(dist, prefix):
if data:
for f in data.get('files', []):
path = join(prefix, f)
if post.is_obj(path):
if is_codefile(path):
res.append(f)

return res
Expand All @@ -96,7 +105,7 @@ def get_untracked_obj_files(prefix):
files = untracked(prefix)
for f in files:
path = join(prefix, f)
if post.is_obj(path):
if is_codefile(path):
res.append(f)

return res
18 changes: 14 additions & 4 deletions conda_build/os_utils/macho.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
from __future__ import absolute_import, division, print_function

import re
import sys
from subprocess import Popen, check_output, PIPE
from subprocess import Popen, check_output, PIPE, STDOUT, CalledProcessError
from os.path import islink, isfile
from conda_build.os_utils.pyldd import inspect_rpaths
from itertools import islice

NO_EXT = (
Expand Down Expand Up @@ -157,7 +159,11 @@ def otool(path, cb_filter=is_dylib_info):
Any key values that can be converted to integers are converted
to integers, the rest are strings.
"""
lines = check_output(['otool', '-l', path]).decode('utf-8').splitlines()
lines = check_output(['otool', '-l', path], stderr=STDOUT).decode('utf-8')
# llvm-objdump returns 0 for some things that are anything but successful completion.
if (re.match('.*(is not a Mach-O|invalid|expected|unexpected).*', lines, re.MULTILINE)):
raise CalledProcessError
lines = lines.splitlines()
return _get_matching_load_commands(lines, cb_filter)


Expand All @@ -178,8 +184,12 @@ def get_id(path):

def get_rpaths(path):
"""Return a list of the dylib rpaths"""
rpaths = otool(path, is_rpath)
return [rpath['path'] for rpath in rpaths]
# rpaths = otool(path, is_rpath)
# res_otool = [rpath['path'] for rpath in rpaths]
res_pyldd = inspect_rpaths(path, resolve_dirnames=False, use_os_varnames=True)
# if set(res_otool) != set(res_pyldd):
# print("disagreement about get_rpaths {} vs {}".format(set(res_otool), set(res_pyldd)))
return res_pyldd


def add_rpath(path, rpath, verbose=False):
Expand Down
Loading