forked from pantsbuild/pants
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Fixes the bug where pants doesn't recognize build targets on top-leve…
…l directories. The previous argument parser naively simply checked whether a command-line argument contained a slash (/) or a colon (:). This is a reasonable assumption most of the time, but in the case where a BUILD file is located only one directory deep, (eg, buildroot/folder/BUILD), this makes it impossible to run pants on the target simply by calling (eg) ./pants goal bundle folder. See github issue for example: pantsbuild#159 This fixes it by actually checking to see if a phase with goals is defined by the command-line argument. It still checks before that whether the argument contains a slash or a colon, because that if an argument has those characters it can't mean a goal. But an argument not having those characters doesn't mean it is a goal. In a completely ambiguous situation (eg, given a command argument 'bundle', and there is a valid BUILD file at bundle/BUILD), we err on the side of the goal, and print out a warning. This makes sense, because it allows the user to disambiguate the situation using the '--' syntax.
- Loading branch information
1 parent
cff740c
commit 4f0bbc9
Showing
4 changed files
with
274 additions
and
23 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,179 @@ | ||
# coding=utf-8 | ||
# Copyright 2014 Pants project contributors (see CONTRIBUTORS.md). | ||
# Licensed under the Apache License, Version 2.0 (see LICENSE). | ||
|
||
from __future__ import (nested_scopes, generators, division, absolute_import, with_statement, | ||
print_function, unicode_literals) | ||
|
||
import os | ||
|
||
from pants.base.config import Config | ||
from pants.base.exceptions import TaskError | ||
from pants.binary_util import select_binary_stream, select_binary_base_path | ||
from pants_test.base_test import BaseTest | ||
|
||
|
||
class BinaryUtilTest(BaseTest): | ||
"""Tests binary_util's pants_support_baseurls handling.""" | ||
|
||
class LambdaReader(object): | ||
"""Class which pretends to be an input stream, but is actually a lambda function.""" | ||
|
||
def __init__(self, func): | ||
self._func = func | ||
|
||
def __call__(self): | ||
return self._func() | ||
|
||
def read(self): | ||
return self() | ||
|
||
def __enter__(self, a=None, b=None, c=None, d=None): | ||
return self | ||
|
||
def __exit__(self, a=None, b=None, c=None, d=None): | ||
pass | ||
|
||
class MapReader(object): | ||
"""Class which pretends to be a url stream opener, but is actually a dictionary.""" | ||
|
||
def __init__(self, read_map): | ||
self._map = read_map | ||
|
||
def __call__(self, key): | ||
if not key in self._map: | ||
raise IOError("404: Virtual URL '{key}' does not exist.".format(key=key)) | ||
value = self._map[key] | ||
return BinaryUtilTest.LambdaReader(lambda: value) | ||
|
||
def keys(self): | ||
return self._map.keys() | ||
|
||
def __getitem__(self, key): | ||
return self._map[key] # Vanilla internal map access (without lambda shenanigans). | ||
|
||
def setUp(self): | ||
super(BinaryUtilTest, self).setUp() | ||
|
||
def config_urls(self, urls=None, legacy=None): | ||
"""Generates the contents of a configuration file.""" | ||
def clean_config(txt): | ||
return '\n'.join((line.strip() for line in txt.split('\n'))) | ||
|
||
if legacy: | ||
legacy = '\npants_support_baseurl: {url}'.format(url=legacy) | ||
else: | ||
legacy = '' | ||
|
||
if urls: | ||
urls = '\npants_support_baseurls = {urls}'.format(urls=urls) | ||
else: | ||
urls = '' | ||
|
||
return self.config(overrides=clean_config('[DEFAULT]{urls}{legacy}'.format(urls=urls, | ||
legacy=legacy))) | ||
|
||
@classmethod | ||
def _fake_base(cls, name): | ||
return 'fake-url-{name}'.format(name=name) | ||
|
||
@classmethod | ||
def _fake_url(cls, binaries, base, binary_key): | ||
base_path, version, name = binaries[binary_key] | ||
return '{base}/{binary}'.format(base=base, | ||
binary=select_binary_base_path(base_path, version, name)) | ||
|
||
def _seens_test(self, binaries, bases, reader, config=None): | ||
unseen = filter(lambda item: item.startswith('SEEN '), (reader[key] for key in reader.keys())) | ||
if not config: | ||
config = self.config_urls(bases) | ||
for key in binaries: | ||
base_path, version, name = binaries[key] | ||
with select_binary_stream(base_path, | ||
version, | ||
name, | ||
config=config, | ||
url_opener=reader) as stream: | ||
self.assertEqual(stream(), 'SEEN ' + key.upper()) | ||
unseen.remove(stream()) | ||
self.assertEqual(0, len(unseen)) # Make sure we've seen all the SEENs. | ||
|
||
def test_legacy_support(self): | ||
"""Tests legacy support for old pants.ini.""" | ||
fake_base, fake_url = self._fake_base, self._fake_url | ||
binaries = { | ||
'protoc': ('bin/protobuf', '2.4.1', 'protoc',), | ||
'ivy': ('bin/ivy', '4.3.7', 'ivy',), | ||
'bash': ('bin/bash', '4.4.3', 'bash',), | ||
} | ||
bases = [fake_base('apple'), fake_base('orange'), fake_base('banana'),] | ||
reader = self.MapReader({ | ||
fake_url(binaries, bases[0], 'protoc'): 'SEEN PROTOC', | ||
fake_url(binaries, bases[0], 'ivy'): 'SEEN IVY', | ||
fake_url(binaries, bases[0], 'bash'): 'SEEN BASH', | ||
}) | ||
config = self.config_urls(legacy=bases[0]) | ||
self._seens_test(binaries, bases, reader, config=config) | ||
|
||
def test_mixed_support(self): | ||
"""Tests support for mixed legacy and new style build support in pants.ini.""" | ||
fake_base, fake_url = self._fake_base, self._fake_url | ||
binaries = { | ||
'protoc': ('bin/protobuf', '2.4.1', 'protoc',), | ||
'ivy': ('bin/ivy', '4.3.7', 'ivy',), | ||
'bash': ('bin/bash', '4.4.3', 'bash',), | ||
} | ||
bases = [fake_base('apple'), fake_base('orange'), fake_base('banana'),] | ||
reader = self.MapReader({ | ||
fake_url(binaries, bases[0], 'protoc'): 'SEEN PROTOC', | ||
fake_url(binaries, bases[1], 'ivy'): 'SEEN IVY', | ||
fake_url(binaries, bases[2], 'bash'): 'SEEN BASH', | ||
}) | ||
config = self.config_urls(urls=bases[1:], legacy=bases[0]) | ||
self._seens_test(binaries, bases, reader, config=config) | ||
|
||
def test_nobases(self): | ||
"""Tests exception handling if build support urls are improperly specified.""" | ||
try: | ||
with select_binary_stream('bin/foo', '4.4.3', 'foo', self.config_urls()) as stream: | ||
self.assertTrue(False) # We should get an exception before we reach here. | ||
except TaskError as e: | ||
self.assertTrue(e.message.find(' defined ') > 0) # Check if we got the right error message. | ||
|
||
def test_support_url_multi(self): | ||
"""Tests to make sure existing base urls function as expected.""" | ||
config = self.config_urls([ | ||
'BLATANTLY INVALID URL', | ||
'https://pantsbuild.github.io/binaries/reasonably-invalid-url', | ||
'https://pantsbuild.github.io/binaries/build-support', | ||
'https://pantsbuild.github.io/binaries/build-support', # Test duplicate entry handling. | ||
'https://pantsbuild.github.io/binaries/another-invalid-url', | ||
]) | ||
binaries = [ | ||
('bin/protobuf', '2.4.1', 'protoc',), | ||
] | ||
for base_path, version, name in binaries: | ||
one = 0 | ||
with select_binary_stream(base_path, version, name, config=config) as stream: | ||
stream() | ||
one += 1 | ||
self.assertEqual(one, 1) | ||
|
||
def test_support_url_fallback(self): | ||
"""Tests fallback behavior with multiple support baseurls.""" | ||
fake_base, fake_url = self._fake_base, self._fake_url | ||
binaries = { | ||
'protoc': ('bin/protobuf', '2.4.1', 'protoc',), | ||
'ivy': ('bin/ivy', '4.3.7', 'ivy',), | ||
'bash': ('bin/bash', '4.4.3', 'bash',), | ||
} | ||
bases = [fake_base('apple'), fake_base('orange'), fake_base('banana'),] | ||
reader = self.MapReader({ | ||
fake_url(binaries, bases[0], 'protoc'): 'SEEN PROTOC', | ||
fake_url(binaries, bases[0], 'ivy'): 'SEEN IVY', | ||
fake_url(binaries, bases[1], 'bash'): 'SEEN BASH', | ||
fake_url(binaries, bases[1], 'protoc'): 'UNSEEN PROTOC 1', | ||
fake_url(binaries, bases[2], 'protoc'): 'UNSEEN PROTOC 2', | ||
fake_url(binaries, bases[2], 'ivy'): 'UNSEEN IVY 2', | ||
}) | ||
self._seens_test(binaries, bases, reader) |