diff --git a/.gitignore b/.gitignore index fa8b814181..40f7dd7eb3 100644 --- a/.gitignore +++ b/.gitignore @@ -9,3 +9,5 @@ tags MANIFEST ve venv +junit.xml +.cache diff --git a/.travis.yml b/.travis.yml index 3a5c95c3e6..dfa9bc5791 100644 --- a/.travis.yml +++ b/.travis.yml @@ -19,7 +19,7 @@ install: - conda config --set always_yes yes - conda update -q --all - conda install -q --force --no-deps conda requests - - conda install -q pip pytest requests jinja2 patchelf flake8 python=$TRAVIS_PYTHON_VERSION pyflakes=1.1 + - conda install -q pip pytest requests jinja2 patchelf flake8 mock python=$TRAVIS_PYTHON_VERSION pyflakes=1.1 - conda install -q anaconda-client conda-build - pip install pytest-cov - python setup.py install diff --git a/appveyor.yml b/appveyor.yml index b0d4e57844..712381ec52 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -60,7 +60,7 @@ install: - python -c "import sys; print(sys.version)" - python -c "import sys; print(sys.executable)" - python -c "import sys; print(sys.prefix)" - - conda install -q pytest pytest-cov requests pycrypto git anaconda-client + - conda install -q pytest pytest-cov mock requests pycrypto git anaconda-client # this is to ensure dependencies - conda install -q conda-build - python --version diff --git a/conda.recipe/meta.yaml b/conda.recipe/meta.yaml index b4ca193fe6..6877a31bcd 100644 --- a/conda.recipe/meta.yaml +++ b/conda.recipe/meta.yaml @@ -25,6 +25,7 @@ test: requires: - pytest - pytest-cov + - mock commands: - conda-build -h imports: diff --git a/conda_build/build.py b/conda_build/build.py index 4f3da8e271..dbf1bda9dd 100644 --- a/conda_build/build.py +++ b/conda_build/build.py @@ -5,6 +5,7 @@ import io import json +import logging import os import shutil import stat @@ -53,6 +54,8 @@ override_channels = False verbose = True +log = logging.getLogger(__file__) + def prefix_files(): ''' @@ -463,21 +466,40 @@ def build(m, post=None, include_recipe=True, keep_old_work=False, rm_rf(config.short_build_prefix) rm_rf(config.long_build_prefix) + specs = [ms.spec for ms in m.ms_depends('build')] + if activate: + # If we activate the build envrionment, we need to be sure that we + # have the appropriate VCS available in the environment. People + # are not used to explicitly listing it in recipes, though. + # We add it for them here, but warn them about it. + vcs_source = m.uses_vcs() + if vcs_source and vcs_source not in specs: + specs.append(vcs_source) + log.warn("Your recipe depends on {} at build time (for templates), " + "but you have not listed it as a build dependency. Doing so for" + " this build.") # Display the name only # Version number could be missing due to dependency on source info. - create_env(config.build_prefix, - [ms.spec for ms in m.ms_depends('build')]) + create_env(config.build_prefix, specs) if need_source_download: # Execute any commands fetching the source (e.g., git) in the _build environment. # This makes it possible to provide source fetchers (eg. git, hg, svn) as build # dependencies. - m, need_source_download = parse_or_try_download(m, - no_download_source=False, - force_download=True, - verbose=verbose, - dirty=dirty) - assert not need_source_download, "Source download failed. Please investigate." + if not activate: + _old_path = os.environ['PATH'] + os.environ['PATH'] = prepend_bin_path({'PATH': _old_path}, + config.build_prefix)['PATH'] + try: + m, need_source_download = parse_or_try_download(m, + no_download_source=False, + force_download=True, + verbose=verbose, + dirty=dirty) + assert not need_source_download, "Source download failed. Please investigate." + finally: + if not activate: + os.environ['PATH'] = _old_path if m.name() in [i.rsplit('-', 2)[0] for i in linked(config.build_prefix)]: print("%s is installed as a build dependency. Removing." % @@ -691,7 +713,6 @@ def test(m, move_broken=True, activate=True): specs += ['lua %s*' % environ.get_lua_ver()] create_env(config.test_prefix, specs) - env = dict(os.environ) env.update(environ.get_dict(m, prefix=config.test_prefix)) diff --git a/conda_build/main_build.py b/conda_build/main_build.py index 9076c71cce..78aecf239f 100644 --- a/conda_build/main_build.py +++ b/conda_build/main_build.py @@ -124,14 +124,18 @@ def main(): ) p.add_argument( '--token', - action="store", help="Token to pass through to anaconda upload" ) p.add_argument( '--user', - action='store', help="User/organization to upload packages to on anaconda.org" ) + p.add_argument( + "--no-activate", + action="store_false", + help="do not display progress bar", + dest='activate', + ) add_parser_channels(p) p.set_defaults(func=execute) @@ -305,7 +309,7 @@ def execute(args, parser): continue elif args.test: build.test(m, move_broken=False) - elif args.source: + elif args.source and need_source_download: source.provide(m.path, m.get_section('source'), verbose=build.verbose) print('Source tree in:', source.get_dir()) else: @@ -325,7 +329,7 @@ def execute(args, parser): include_recipe=args.include_recipe, keep_old_work=args.keep_old_work, need_source_download=need_source_download, - dirty=args.dirty) + dirty=args.dirty, activate=args.activate) except (NoPackagesFound, Unsatisfiable) as e: error_str = str(e) # Typically if a conflict is with one of these @@ -365,7 +369,7 @@ def execute(args, parser): continue if not args.notest: - build.test(m) + build.test(m, activate=args.activate) if need_cleanup: shutil.rmtree(recipe_dir) diff --git a/conda_build/metadata.py b/conda_build/metadata.py index 6f4566aab1..b302624e03 100644 --- a/conda_build/metadata.py +++ b/conda_build/metadata.py @@ -371,6 +371,7 @@ def parse_again(self, permit_undefined_jinja=False): """ if not self.meta_path: return + self.meta = parse(self._get_contents(permit_undefined_jinja), path=self.meta_path) if (isfile(self.requirements_path) and @@ -693,3 +694,28 @@ def __repr__(self): String representation of the MetaData. ''' return self.__str__() + + def uses_vcs(self): + """returns true if recipe contains metadata associated with version control systems. + If this metadata is present, a download/copy will be forced in parse_or_try_download. + """ + vcs_types = ["git", "svn", "hg"] + if "source" in self.meta: + for vcs in vcs_types: + if vcs + "_url" in self.meta["source"]: + # translate command name to package name. + # If more than hg, need a dict for this. + if vcs == "hg": + vcs = "mercurial" + return vcs + + # We would get here if we use Jinja2 templating, but specify source with path. + with open(self.meta_path) as f: + metayaml = f.read() + for vcs in vcs_types: + matches = re.findall(r"{}_[^\.\s\'\"]+".format(vcs.upper()), metayaml) + if len(matches) > 0: + if vcs == "hg": + vcs = "mercurial" + return vcs + return None diff --git a/conda_build/render.py b/conda_build/render.py index 8e767e06df..f048990abe 100644 --- a/conda_build/render.py +++ b/conda_build/render.py @@ -75,20 +75,10 @@ def bldpkg_path(m): return os.path.join(config.bldpkgs_dir, '%s.tar.bz2' % m.dist()) -def has_vcs_metadata(metadata): - """returns true if recie contains metadata associated with version control systems. - If this metadata is present, a download/copy will be forced in parse_or_try_download. - """ - with open(metadata.meta_path) as f: - matches = re.findall(r"GIT_[^\.\s\'\"]+", f.read()) - # TODO: extend with other VCS systems (SVN, hg, anything else?) - return len(matches) > 0 - - def parse_or_try_download(metadata, no_download_source, verbose, force_download=False, dirty=False): - if (force_download or (not no_download_source and has_vcs_metadata(metadata))): + if (force_download or (not no_download_source and metadata.uses_vcs())): # this try/catch is for when the tool to download source is actually in # meta.yaml, and not previously installed in builder env. try: @@ -140,13 +130,13 @@ def render_recipe(recipe_path, no_download_source, verbose, dirty=False): sys.stderr.write(e.error_msg()) sys.exit(1) - m = parse_or_try_download(m, no_download_source=no_download_source, + m, need_download = parse_or_try_download(m, no_download_source=no_download_source, verbose=verbose, dirty=dirty) if need_cleanup: shutil.rmtree(recipe_dir) - return m + return m, need_download # Next bit of stuff is to support YAML output in the order we expect. diff --git a/conda_build/windows.py b/conda_build/windows.py index 3b06d81b5a..a992b8a86d 100644 --- a/conda_build/windows.py +++ b/conda_build/windows.py @@ -1,10 +1,16 @@ from __future__ import absolute_import, division, print_function import os +import re import sys import shutil +import subprocess from os.path import dirname, isdir, isfile, join +# Leverage the hard work done by setuptools/distutils to find vcvarsall using +# either the registry or the VS**COMNTOOLS environment variable +from distutils.msvc9compiler import query_vcvarsall + import conda.config as cc from conda_build.config import config @@ -12,27 +18,9 @@ from conda_build import source from conda_build.utils import _check_call + assert sys.platform == 'win32' -# Set up a load of paths that can be imported from the tests -if 'ProgramFiles(x86)' in os.environ: - PROGRAM_FILES_PATH = os.environ['ProgramFiles(x86)'] -else: - PROGRAM_FILES_PATH = os.environ['ProgramFiles'] - -# Note that we explicitly want "Program Files" and not "Program Files (x86)" -WIN_SDK_BAT_PATH = os.path.join(PROGRAM_FILES_PATH.replace(" (x86)", ""), - 'Microsoft SDKs', 'Windows', 'v7.1', 'Bin', 'SetEnv.cmd') -VS_TOOLS_PY_LOCAL_PATH = os.path.join( - os.getenv('localappdata', os.path.abspath(os.sep)), - 'Programs', 'Common', 'Microsoft', 'Visual C++ for Python', '9.0', - 'vcvarsall.bat') -VS_TOOLS_PY_COMMON_PATH = os.path.join(PROGRAM_FILES_PATH, 'Common Files', - 'Microsoft', 'Visual C++ for Python', - '9.0', 'vcvarsall.bat') -VCVARS64_VS9_BAT_PATH = os.path.join(PROGRAM_FILES_PATH, - 'Microsoft Visual Studio 9.0', 'VC', 'bin', - 'vcvars64.bat') VS_VERSION_STRING = { '8.0': 'Visual Studio 8 2005', '9.0': 'Visual Studio 9 2008', @@ -43,23 +31,6 @@ } -def build_vcvarsall_vs_path(version): - """ - Given the Visual Studio version, returns the default path to the - Microsoft Visual Studio vcvarsall.bat file. - - Expected versions are of the form {9, 10, 12, 14} - """ - vstools = "VS{0}0COMNTOOLS".format(version) - if vstools in os.environ: - return os.path.join(os.environ[vstools], '..\\..\\VC\\vcvarsall.bat') - else: - # prefer looking at env var; fall back to program files defaults - return os.path.join(PROGRAM_FILES_PATH, - 'Microsoft Visual Studio {}'.format(version), 'VC', - 'vcvarsall.bat') - - def fix_staged_scripts(): """ Fixes scripts which have been installed unix-style to have a .bat @@ -94,13 +65,20 @@ def fix_staged_scripts(): def msvc_env_cmd(bits, override=None): arch_selector = 'x86' if bits == 32 else 'amd64' - msvc_env_lines = [] + compiler_vars = {} version = None - if override is not None: + if override: version = override - msvc_env_lines.append('set DISTUTILS_USE_SDK=1') - msvc_env_lines.append('set MSSdk=1') + # The DISTUTILS_USE_SDK variable tells distutils to not try and validate + # the MSVC compiler. For < 3.5 this still forcibly looks for 'cl.exe'. + # For > 3.5 it literally just skips the validation logic. + # See distutils _msvccompiler.py and msvc9compiler.py / msvccompiler.py + # for more information. + compiler_vars.update({"DISTUTILS_USE_SDK": 1, + # This is also required to hit the 'don't validate' logic on < 3.5. + # For > 3.5 this is ignored. + "MSSdk": 1}) if not version: if config.PY3K and config.use_MSVC2015: @@ -110,57 +88,20 @@ def msvc_env_cmd(bits, override=None): else: version = '9.0' - vcvarsall_vs_path = build_vcvarsall_vs_path(version) - - def build_vcvarsall_cmd(cmd, arch=arch_selector): - return 'call "{cmd}" {arch}'.format(cmd=cmd, arch=arch) - - msvc_env_lines.append('set "VS_VERSION={}"'.format(version)) - msvc_env_lines.append('set "VS_MAJOR={}"'.format(version.split('.')[0])) - msvc_env_lines.append('set "VS_YEAR={}"'.format(VS_VERSION_STRING[version][-4:])) - msvc_env_lines.append('set "CMAKE_GENERATOR={}"'.format(VS_VERSION_STRING[version] + - {64: ' Win64', 32: ''}[bits])) - # tell msys2 to ignore path conversions for issue-causing windows-style flags in build - # See https://github.com/conda-forge/icu-feedstock/pull/5 - msvc_env_lines.append('set "MSYS2_ARG_CONV_EXCL=/AI;/AL;/OUT;/out;%MSYS2_ARG_CONV_EXCL%"') - msvc_env_lines.append('set "MSYS2_ENV_CONV_EXCL=CL"') - if version == '10.0': - win_sdk_arch = '/Release /x86' if bits == 32 else '/Release /x64' - win_sdk_cmd = build_vcvarsall_cmd(WIN_SDK_BAT_PATH, arch=win_sdk_arch) - - # Always call the Windows SDK first - if VS 2010 exists but was - # installed using the broken installer then it will try and call the - # vcvars script, which will fail but NOT EXIT 1. To work around this, - # we always call the Windows SDK, and then try calling VS 2010 which - # will overwrite any environemnt variables it needs, if necessary. - msvc_env_lines.append(win_sdk_cmd) - msvc_env_lines.append(build_vcvarsall_cmd(vcvarsall_vs_path)) - - elif version == '9.0': - error1 = 'if errorlevel 1 {}' - - # First, check for Microsoft Visual C++ Compiler for Python 2.7 - msvc_env_lines.append(build_vcvarsall_cmd(VS_TOOLS_PY_LOCAL_PATH)) - msvc_env_lines.append(error1.format( - build_vcvarsall_cmd(VS_TOOLS_PY_COMMON_PATH))) - # The Visual Studio 2008 Express edition does not properly contain - # the amd64 build files, so we call the vcvars64.bat manually, - # rather than using the vcvarsall.bat which would try and call the - # missing bat file. - if arch_selector == 'amd64': - msvc_env_lines.append(error1.format( - build_vcvarsall_cmd(VCVARS64_VS9_BAT_PATH))) - msvc_env_lines.append(error1.format( - build_vcvarsall_cmd(vcvarsall_vs_path))) - else: - msvc_env_lines.append(error1.format( - build_vcvarsall_cmd(vcvarsall_vs_path))) - - else: - # Visual Studio 14 or otherwise - msvc_env_lines.append(build_vcvarsall_cmd(vcvarsall_vs_path)) + compiler_vars.update({ + "VS_VERSION": version, + "VS_MAJOR": version.split('.')[0], + "VS_YEAR": VS_VERSION_STRING[version][-4:], + "CMAKE_GENERATOR": VS_VERSION_STRING[version] + {64: ' Win64', 32: ''}[bits], + # tell msys2 to ignore path conversions for issue-causing windows-style flags in build + # See https://github.com/conda-forge/icu-feedstock/pull/5 + "MSYS2_ARG_CONV_EXCL": "/AI;/AL;/OUT;/out;%MSYS2_ARG_CONV_EXCL%", + "MSYS2_ENV_CONV_EXCL": "CL;%MSYS2_ENV_CONV_EXCL%", + }) - return '\n'.join(msvc_env_lines) + '\n' + captured_vars = query_vcvarsall(float(version), arch_selector) + compiler_vars.update(captured_vars) + return compiler_vars def kill_processes(process_names=["msbuild.exe"]): @@ -185,6 +126,18 @@ def kill_processes(process_names=["msbuild.exe"]): except: continue +def _merge_dicts(d1, d2): + """Merges d2's contents into d1. Unlike update, this keeps all entries of both, by performing + unions of values.""" + for key, value in d1.items(): + if key in d2: + combined = set(value.split(';')) + combined.update(set(d2[key].split(';'))) + d1[key] = ";".join(combined) + # delete it. We'll merge remaining vars at the end. + del d2[key] + d1.update(d2) + return d1 def build(m, bld_bat, dirty=False, activate=True): env = environ.get_dict(m, dirty=dirty) @@ -201,17 +154,24 @@ def build(m, bld_bat, dirty=False, activate=True): with open(join(src_dir, 'bld.bat'), 'w') as fo: # more debuggable with echo on fo.write('@echo on\n') + + compiler_vars = msvc_env_cmd(bits=cc.bits, override=m.get_value('build/msvc_compiler', None)) + # ensure that all values are uppercase, for sake of merge. + env = {key.upper(): value for key, value in env.items()} + compiler_vars = {key.upper(): value for key, value in compiler_vars.items()} + + # this is a union of all values from env and from compiler vars. env should take priority. + env = _merge_dicts(env, compiler_vars) + for key, value in env.items(): fo.write('set "{key}={value}"\n'.format(key=key, value=value)) fo.write("set INCLUDE={};%INCLUDE%\n".format(env["LIBRARY_INC"])) fo.write("set LIB={};%LIB%\n".format(env["LIBRARY_LIB"])) - fo.write(msvc_env_cmd(bits=cc.bits, override=m.get_value('build/msvc_compiler', None))) - if activate: - fo.write("call activate _build\n") fo.write('\n') fo.write("REM ===== end generated header =====\n") fo.write(data) + cmd = [os.environ['COMSPEC'], '/c', 'call', 'bld.bat'] _check_call(cmd, cwd=src_dir) kill_processes() diff --git a/tests/test-recipes/metadata/_build_msvc_compiler/bld.bat b/tests/test-recipes/metadata/_build_msvc_compiler/bld.bat new file mode 100644 index 0000000000..c3b66aab5f --- /dev/null +++ b/tests/test-recipes/metadata/_build_msvc_compiler/bld.bat @@ -0,0 +1,16 @@ +@echo ON + +IF %DISTUTILS_USE_SDK% NEQ 1 ( EXIT 1 ) +IF %MSSdk% NEQ 1 ( EXIT 1 ) +IF NOT "%VS_VERSION%" == "%CONDATEST_MSVC_VER%" ( EXIT 1 ) +IF NOT "%PY_VER%" == "2.7" ( EXIT 1 ) + +REM Run cl.exe to find which version our compiler is +REM First picks out the version line +for /f "delims=" %%A in ('cl /? 2^>^&1 ^| findstr /C:"Version"') do set "CL_TEXT=%%A" +REM Looks for the known version in that version line +echo %CL_TEXT% | findstr /C:"Version %CL_EXE_VERSION%" > nul && goto FOUND +REM only falls through here if things don't match +EXIT 1 +:FOUND +exit 0 \ No newline at end of file diff --git a/tests/test-recipes/metadata/_build_msvc_compiler/meta.yaml b/tests/test-recipes/metadata/_build_msvc_compiler/meta.yaml new file mode 100644 index 0000000000..8e08926910 --- /dev/null +++ b/tests/test-recipes/metadata/_build_msvc_compiler/meta.yaml @@ -0,0 +1,12 @@ +package: + name: conda-build-test-build-msvc-compiler + version: 1.0 + +source: + path: ../../../../ + +build: + msvc_compiler: {{ environ.get('CONDATEST_MSVC_VER') }} + script_env: + - CONDATEST_MSVC_VER + - CL_EXE_VERSION diff --git a/tests/test-recipes/metadata/_build_msvc_compiler/run_test.bat b/tests/test-recipes/metadata/_build_msvc_compiler/run_test.bat new file mode 100644 index 0000000000..5737293b0d --- /dev/null +++ b/tests/test-recipes/metadata/_build_msvc_compiler/run_test.bat @@ -0,0 +1,8 @@ +conda list -p "%PREFIX%" --canonical +if errorlevel 1 exit 1 +for /f "delims=" %%i in ('conda list -p "%PREFIX%" --canonical') do set condalist=%%i +if errorlevel 1 exit 1 +echo "%condalist%" +if not "%condalist%"=="conda-build-test-build-msvc-compiler-1.0-0" exit 1 +type "%PREFIX%\conda-meta\conda-build-test-build-msvc-compiler-1.0-0.json" +if errorlevel 1 exit 1 diff --git a/tests/test-recipes/metadata/_set_env_var_no_activate_build/bld.bat b/tests/test-recipes/metadata/_set_env_var_no_activate_build/bld.bat new file mode 100644 index 0000000000..8ca293e575 --- /dev/null +++ b/tests/test-recipes/metadata/_set_env_var_no_activate_build/bld.bat @@ -0,0 +1,2 @@ +if "%TEST_VAR%" == "" exit 0 +exit 1 \ No newline at end of file diff --git a/tests/test-recipes/metadata/_set_env_var_no_activate_build/build.sh b/tests/test-recipes/metadata/_set_env_var_no_activate_build/build.sh new file mode 100644 index 0000000000..fa5d9fa362 --- /dev/null +++ b/tests/test-recipes/metadata/_set_env_var_no_activate_build/build.sh @@ -0,0 +1,4 @@ +if [ -z "$TEST_VAR" ]; then + exit 0 +fi +exit 1 diff --git a/tests/test-recipes/metadata/_set_env_var_no_activate_build/meta.yaml b/tests/test-recipes/metadata/_set_env_var_no_activate_build/meta.yaml new file mode 100644 index 0000000000..00d57a8bc2 --- /dev/null +++ b/tests/test-recipes/metadata/_set_env_var_no_activate_build/meta.yaml @@ -0,0 +1,11 @@ +package: + name: conda-build-test-environment-vars-in-build-env + version: 1.0 + +requirements: + build: + # having this as a build requirement should use the activate scripts that it contains + - _conda-build-test-environment-vars-in-build-env + +about: + summary: test that scripts with activate.d and deactivate.d provide environment variables appropriately diff --git a/tests/test-recipes/metadata/jinja2_build_str_template_only/bld.bat b/tests/test-recipes/metadata/jinja2_build_str_template_only/bld.bat index ca1489a42c..2590fefec1 100644 --- a/tests/test-recipes/metadata/jinja2_build_str_template_only/bld.bat +++ b/tests/test-recipes/metadata/jinja2_build_str_template_only/bld.bat @@ -1,20 +1,4 @@ -if not exist .git exit 1 -git config core.fileMode false -if errorlevel 1 exit 1 -git describe --tags --dirty -if errorlevel 1 exit 1 -for /f "delims=" %%i in ('git describe') do set gitdesc=%%i -if errorlevel 1 exit 1 -echo "%gitdesc%" -if not "%gitdesc%"=="1.8.1" exit 1 -git status -if errorlevel 1 exit 1 -git diff -if errorlevel 1 exit 1 -set PYTHONPATH=. -python -c "import conda_build; assert conda_build.__version__ == '1.8.1', conda_build.__version__" -if errorlevel 1 exit 1 - +set PATH rem check that GIT_* tags are present for %%i in (GIT_DESCRIBE_TAG GIT_DESCRIBE_NUMBER GIT_DESCRIBE_HASH GIT_FULL_HASH) DO ( diff --git a/tests/test-recipes/metadata/jinja2_build_str_template_only/build.sh b/tests/test-recipes/metadata/jinja2_build_str_template_only/build.sh index 74571375c8..42f3b09ae7 100644 --- a/tests/test-recipes/metadata/jinja2_build_str_template_only/build.sh +++ b/tests/test-recipes/metadata/jinja2_build_str_template_only/build.sh @@ -1,11 +1,3 @@ -# We test the environment variables in a different recipe - -# Ensure we are in a git repo -[ -d .git ] -git describe -[ "$(git describe)" = 1.8.1 ] -PYTHONPATH=. python -c "import conda_build; assert conda_build.__version__ == '1.8.1', conda_build.__version__" - # check if GIT_* variables are defined for i in GIT_DESCRIBE_TAG GIT_DESCRIBE_NUMBER GIT_DESCRIBE_HASH GIT_FULL_HASH do diff --git a/tests/test-recipes/metadata/jinja2_build_str_template_only/meta.yaml b/tests/test-recipes/metadata/jinja2_build_str_template_only/meta.yaml index 032b759b2f..923af176d4 100644 --- a/tests/test-recipes/metadata/jinja2_build_str_template_only/meta.yaml +++ b/tests/test-recipes/metadata/jinja2_build_str_template_only/meta.yaml @@ -4,7 +4,6 @@ package: source: git_url: ../../../../ - git_tag: 1.8.1 build: string: {{ PKG_BUILDNUM }}_g{{ GIT_FULL_HASH[:7] }} @@ -13,3 +12,4 @@ requirements: build: # To test the conda_build version - python + - git diff --git a/tests/test_build_recipes.py b/tests/test_build_recipes.py index 1a9e9d5aa3..7df353c140 100644 --- a/tests/test_build_recipes.py +++ b/tests/test_build_recipes.py @@ -12,8 +12,8 @@ from conda_build.source import _guess_patch_strip_level, apply_patch thisdir = os.path.dirname(os.path.realpath(__file__)) -metadata_dir = os.path.join(thisdir, "test-recipes/metadata") -fail_dir = os.path.join(thisdir, "test-recipes/fail") +metadata_dir = os.path.join(thisdir, 'test-recipes', 'metadata') +fail_dir = os.path.join(thisdir, 'test-recipes', 'fail') def is_valid_dir(parent_dir, dirname): @@ -48,6 +48,12 @@ def test_output_build_path_git_source(): assert output.rstrip() == test_path, error +def test_build_with_no_activate_does_not_activate(): + cmd = ('conda build --no-anaconda-upload --no-activate ' + '{}/_set_env_var_no_activate_build').format(metadata_dir) + subprocess.check_call(cmd.split(), cwd=metadata_dir) + + @pytest.mark.skipif(sys.platform == "win32", reason="no binary prefix manipulation done on windows.") def test_binary_has_prefix_files(): @@ -191,10 +197,32 @@ def test_checkout_tool_as_dependency(): if sys.platform == "win32": platforms = set(["32", ] + platforms) compilers = ["2.7", "3.4", "3.5"] + msvc_vers = ['9.0', '10.0', '14.0'] else: + msvc_vers = [] compilers = [".".join([str(sys.version_info.major), str(sys.version_info.minor)])] +@pytest.mark.skipif(sys.platform != "win32", reason="MSVC only on windows") +@pytest.mark.parametrize("msvc_ver", msvc_vers) +def test_build_msvc_compiler(msvc_ver): + env = dict(os.environ) + # verify that the correct compiler is available + cl_versions = {"9.0": 15, + "10.0": 16, + "11.0": 17, + "12.0": 18, + "14.0": 19} + + env['CONDATEST_MSVC_VER'] = msvc_ver + env['CL_EXE_VERSION'] = str(cl_versions[msvc_ver]) + + # Always build Python 2.7 - but set MSVC version manually via Jinja template + cmd = 'conda build {} --python=2.7 --no-anaconda-upload'.format( + os.path.join(metadata_dir, '_build_msvc_compiler')) + subprocess.check_call(cmd.split(), env=env) + + @pytest.mark.parametrize("platform", platforms) @pytest.mark.parametrize("target_compiler", compilers) def test_cmake_generator(platform, target_compiler): diff --git a/tests/test_win_vs_activate.py b/tests/test_win_vs_activate.py deleted file mode 100644 index 1f255f41f3..0000000000 --- a/tests/test_win_vs_activate.py +++ /dev/null @@ -1,115 +0,0 @@ -from __future__ import print_function - -import os -import subprocess -import sys - -import pytest - -vcvars_backup_files = {} -if sys.platform == "win32": - from conda_build.windows import (build_vcvarsall_vs_path, - VCVARS64_VS9_BAT_PATH, - VS_TOOLS_PY_LOCAL_PATH, - VS_TOOLS_PY_COMMON_PATH) - - vcvars_backup_files = {"vs{}".format(version): [build_vcvarsall_vs_path(version)] - for version in ["9.0", "10.0", "14.0"]} - vcvars_backup_files['vs9.0'].append(VCVARS64_VS9_BAT_PATH) - # VC9 compiler for python - local user install - vcvars_backup_files["python_local"] = [VS_TOOLS_PY_LOCAL_PATH] - # VC9 compiler for python - common files - vcvars_backup_files["python_system"] = [VS_TOOLS_PY_COMMON_PATH] - - vs9 = {key: vcvars_backup_files[key] for key in ['vs9.0', 'python_local', 'python_system']} - vs10 = {key: vcvars_backup_files[key] for key in ['vs10.0']} - vs14 = {key: vcvars_backup_files[key] for key in ['vs14.0']} - - vcs = {"9.0": vs9, "10.0": vs10, "14.0": vs14} - - -def write_bat_files(good_locations): - for label, locations in vcvars_backup_files.items(): - for location in locations: - # these should all have been moved! bad to overwrite them! - assert not os.path.exists(location) - if not os.path.isdir(os.path.dirname(location)): - # if any of these are made, they are not currently cleaned up. Sorry. - os.makedirs(os.path.dirname(location)) - with open(location, "w") as f: - print("writing {} (exit /b {})".format(location, int(label not in good_locations))) - f.write(":: NOTE: exit code of 1 here means incorrect VS version activated. " - "check logic.\n") - f.write("exit /b {}\n".format(int(label not in good_locations))) - - -@pytest.fixture(scope="function") -def setup_teardown(request): - def fin(): - for locations in vcvars_backup_files.values(): - for location in locations: - # clean up any of the custom scripts we wrote to test - if os.path.exists(location): - os.remove(location) - # restore the backups - if os.path.exists(location[:-1] + 'k'): - os.rename(location[:-1] + 'k', location) - request.addfinalizer(fin) - - # backup known files - for locations in vcvars_backup_files.values(): - for location in locations: - if os.path.exists(location): - os.rename(location, location[:-1] + 'k') - - return request - - -@pytest.fixture(scope="function", params=vcvars_backup_files.keys()) -def compiler(request, setup_teardown): - return request.param - - -@pytest.fixture(params=[32, 64]) -def bits(request): - return request.param - - -@pytest.mark.skipif(sys.platform != "win32", reason="windows-only test") -@pytest.mark.xfail(reason="verification of test logic", strict=True) -def test_activation_logic(bits, compiler): - from conda_build.windows import msvc_env_cmd - # empty list here means no configuration is valid. We should get a - # failure. - write_bat_files([]) - # look up which VS version we're forcing here - compiler_version = [key for key in vcs if compiler in vcs[key]][0] - with open('tmp_call.bat', "w") as f: - f.write(msvc_env_cmd(bits, compiler_version)) - subprocess.check_call(['cmd.exe', '/C', 'tmp_call.bat'], shell=True) - - -@pytest.mark.skipif(sys.platform != "win32", reason="windows-only test") -def test_activation(bits, compiler): - write_bat_files([compiler]) - from conda_build.windows import msvc_env_cmd, VS_VERSION_STRING - # look up which VS version we're forcing here - compiler_version = [key for key in vcs if compiler in vcs[key]][0] - # this will throw an exception if the subprocess return code is not 0 - # this is effectively the test condition for all below tests. - with open('tmp_call.bat', "w") as f: - f.write(msvc_env_cmd(bits, compiler_version)) - f.write('\nif not "%VS_VERSION%" == "{}" exit /b 1'.format(compiler_version)) - f.write('\nif not "%VS_MAJOR%" == "{}" exit /b 1'.format(compiler_version.split('.')[0])) - f.write('\nif not "%VS_YEAR%" == "{}" exit /b 1' - .format(VS_VERSION_STRING[compiler_version][-4:])) - f.write('\nif not "%CMAKE_GENERATOR%" == "{}" exit /b 1' - .format(VS_VERSION_STRING[compiler_version] + - {64: ' Win64', 32: ''}[bits])) - try: - subprocess.check_call(['cmd.exe', '/C', 'tmp_call.bat'], shell=True) - except subprocess.CalledProcessError: - print("failed activation: {}, {}".format(bits, compiler)) - raise - finally: - os.remove('tmp_call.bat')