Skip to content

Commit de26657

Browse files
committed
Merge pull request #2773 from rintaro/build-script-args-type
[build-script] Modularize argparse types
2 parents 841d391 + 7695128 commit de26657

File tree

3 files changed

+202
-57
lines changed

3 files changed

+202
-57
lines changed

utils/build-script

Lines changed: 12 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,6 @@ import multiprocessing
1616
import os
1717
import pipes
1818
import platform
19-
import re
20-
import shlex
2119
import sys
2220

2321
# FIXME: Instead of modifying the system path in order to enable imports from
@@ -40,6 +38,7 @@ from SwiftBuildSupport import (
4038
sys.path.append(os.path.join(os.path.dirname(__file__), 'swift_build_support'))
4139

4240
# E402 means module level import not at top of file
41+
from swift_build_support import arguments # noqa (E402)
4342
from swift_build_support.toolchain import host_toolchain # noqa (E402)
4443
import swift_build_support.debug # noqa (E402)
4544
from swift_build_support import migration # noqa (E402)
@@ -52,50 +51,6 @@ from swift_build_support.workspace import Workspace # noqa(E402)
5251
from swift_build_support.workspace import compute_build_subdir # noqa(E402)
5352

5453

55-
# A strict parser for bools (unlike Python's `bool()`, where
56-
# `bool('False')` is `True`).
57-
#
58-
# This function can be passed as `type=` argument to argparse to parse values
59-
# passed to command line arguments.
60-
def argparse_bool(string):
61-
if string in ['0', 'false', 'False']:
62-
return False
63-
if string in ['1', 'true', 'True']:
64-
return True
65-
raise argparse.ArgumentTypeError("%r is not a boolean value" % string)
66-
67-
68-
# Parse and split shell arguments string into a list of shell arguments.
69-
# Recognize `,` as a separator as well as white spaces.
70-
# string: -BAR="foo bar" -BAZ='foo,bar',-QUX 42
71-
# into
72-
# ['-BAR=foo bar', '-BAZ=foo,bar', "-QUX", "42"]
73-
def argparse_shell_split(string):
74-
lex = shlex.shlex(string, posix=True)
75-
lex.whitespace_split = True
76-
lex.whitespace += ','
77-
return list(lex)
78-
79-
80-
# Parse version string and split into a tuple of strings (major, minor, patch)
81-
# Support only "MAJOR.MINOR.PATCH" format.
82-
def argparse_clang_compiler_version(string):
83-
m = re.match(r'([0-9]*)\.([0-9]*)\.([0-9]*)', string)
84-
if m is not None:
85-
return m.group(1, 2, 3)
86-
raise argparse.ArgumentTypeError(
87-
"%r is invalid version value. must be 'MAJOR.MINOR.PATCH'" % string)
88-
89-
90-
# Check the string is executable path string.
91-
# Convert it to absolute path.
92-
def argparse_executable(string):
93-
if os.access(string, os.X_OK):
94-
return os.path.abspath(string)
95-
raise argparse.ArgumentTypeError(
96-
"%r is not executable" % string)
97-
98-
9954
# Main entry point for the preset mode.
10055
def main_preset():
10156
parser = argparse.ArgumentParser(
@@ -595,7 +550,7 @@ details of the setups of other systems or automated environments.""")
595550
help="test Swift after building",
596551
metavar="BOOL",
597552
nargs='?',
598-
type=argparse_bool,
553+
type=arguments.type.bool,
599554
default=False,
600555
const=True)
601556
run_tests_group.add_argument(
@@ -609,7 +564,7 @@ details of the setups of other systems or automated environments.""")
609564
help="run the validation test suite (implies --test)",
610565
metavar="BOOL",
611566
nargs='?',
612-
type=argparse_bool,
567+
type=arguments.type.bool,
613568
default=False,
614569
const=True)
615570
run_tests_group.add_argument(
@@ -623,15 +578,15 @@ details of the setups of other systems or automated environments.""")
623578
help="run the test suite in optimized mode too (implies --test)",
624579
metavar="BOOL",
625580
nargs='?',
626-
type=argparse_bool,
581+
type=arguments.type.bool,
627582
default=False,
628583
const=True)
629584
run_tests_group.add_argument(
630585
"--long-test",
631586
help="run the long test suite",
632587
metavar="BOOL",
633588
nargs='?',
634-
type=argparse_bool,
589+
type=arguments.type.bool,
635590
default=False,
636591
const=True)
637592
run_tests_group.add_argument(
@@ -864,7 +819,7 @@ details of the setups of other systems or automated environments.""")
864819
"--cmake",
865820
help="the path to a CMake executable that will be used to build "
866821
"Swift",
867-
type=argparse_executable,
822+
type=arguments.type.executable,
868823
metavar="PATH")
869824
parser.add_argument(
870825
"--show-sdks",
@@ -920,13 +875,13 @@ details of the setups of other systems or automated environments.""")
920875
"--host-cc",
921876
help="the absolute path to CC, the 'clang' compiler for the host "
922877
"platform. Default is auto detected.",
923-
type=argparse_executable,
878+
type=arguments.type.executable,
924879
metavar="PATH")
925880
parser.add_argument(
926881
"--host-cxx",
927882
help="the absolute path to CXX, the 'clang++' compiler for the host "
928883
"platform. Default is auto detected.",
929-
type=argparse_executable,
884+
type=arguments.type.executable,
930885
metavar="PATH")
931886
parser.add_argument(
932887
"--distcc",
@@ -943,7 +898,7 @@ details of the setups of other systems or automated environments.""")
943898
parser.add_argument(
944899
"--clang-compiler-version",
945900
help="string that indicates a compiler version for Clang",
946-
type=argparse_clang_compiler_version,
901+
type=arguments.type.clang_compiler_version,
947902
metavar="MAJOR.MINOR.PATCH")
948903

949904
parser.add_argument(
@@ -973,23 +928,23 @@ details of the setups of other systems or automated environments.""")
973928
"separated options '-DCMAKE_VAR1=YES,-DCMAKE_VAR2=/tmp'. Can be "
974929
"called multiple times to add multiple such options.",
975930
action="append",
976-
type=argparse_shell_split,
931+
type=arguments.type.shell_split,
977932
default=[])
978933

979934
parser.add_argument(
980935
"--build-args",
981936
help="arguments to the build tool. This would be prepended to the "
982937
"default argument that is '-j8' when CMake generator is "
983938
"\"Ninja\".",
984-
type=argparse_shell_split,
939+
type=arguments.type.shell_split,
985940
default=[])
986941

987942
parser.add_argument(
988943
"--verbose-build",
989944
help="print the commands executed during the build",
990945
metavar="BOOL",
991946
nargs='?',
992-
type=argparse_bool,
947+
type=arguments.type.bool,
993948
default=False,
994949
const=True)
995950

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
# swift_build_support/arguments.py ------------------------------*- python -*-
2+
#
3+
# This source file is part of the Swift.org open source project
4+
#
5+
# Copyright (c) 2014 - 2016 Apple Inc. and the Swift project authors
6+
# Licensed under Apache License v2.0 with Runtime Library Exception
7+
#
8+
# See http://swift.org/LICENSE.txt for license information
9+
# See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
#
11+
# ----------------------------------------------------------------------------
12+
"""
13+
argparse suppliments
14+
"""
15+
# ----------------------------------------------------------------------------
16+
17+
from __future__ import absolute_import
18+
19+
import argparse
20+
import os
21+
import re
22+
import shlex
23+
24+
__all__ = [
25+
"type"
26+
]
27+
28+
29+
class _Registry(object):
30+
pass
31+
32+
33+
def _register(registry, name, value):
34+
setattr(registry, name, value)
35+
36+
37+
type = _Registry()
38+
39+
40+
def type_bool(string):
41+
"""
42+
A strict parser for bools
43+
44+
unlike Python's `bool()`, where `bool('False')` is `True`
45+
This function can be passed as `type=` argument to argparse to parse values
46+
passed to command line arguments.
47+
"""
48+
if string in ['0', 'false', 'False']:
49+
return False
50+
if string in ['1', 'true', 'True']:
51+
return True
52+
raise argparse.ArgumentTypeError("%r is not a boolean value" % string)
53+
54+
_register(type, 'bool', type_bool)
55+
56+
57+
def type_shell_split(string):
58+
"""
59+
Parse and split shell arguments string into a list of shell arguments.
60+
61+
Recognize `,` as a separator as well as white spaces.
62+
string: -BAR="foo bar" -BAZ='foo,bar',-QUX 42
63+
into
64+
['-BAR=foo bar', '-BAZ=foo,bar', "-QUX", "42"]
65+
"""
66+
lex = shlex.shlex(string, posix=True)
67+
lex.whitespace_split = True
68+
lex.whitespace += ','
69+
return list(lex)
70+
71+
_register(type, 'shell_split', type_shell_split)
72+
73+
74+
def type_clang_compiler_version(string):
75+
"""
76+
Parse version string and split into a tuple of strings
77+
(major, minor, patch)
78+
79+
Support only "MAJOR.MINOR.PATCH" format.
80+
"""
81+
m = re.match(r'^([0-9]+)\.([0-9]+)\.([0-9]+)$', string)
82+
if m is not None:
83+
return m.group(1, 2, 3)
84+
raise argparse.ArgumentTypeError(
85+
"%r is invalid version value. must be 'MAJOR.MINOR.PATCH'" % string)
86+
87+
_register(type, 'clang_compiler_version', type_clang_compiler_version)
88+
89+
90+
def type_executable(string):
91+
"""
92+
Check the string is executable path string.
93+
94+
Convert it to absolute path.
95+
"""
96+
if os.path.isfile(string) and os.access(string, os.X_OK):
97+
return os.path.abspath(string)
98+
raise argparse.ArgumentTypeError(
99+
"%r is not executable" % string)
100+
101+
_register(type, 'executable', type_executable)
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
# tests/arguments.py --------------------------------------------*- python -*-
2+
#
3+
# This source file is part of the Swift.org open source project
4+
#
5+
# Copyright (c) 2014 - 2016 Apple Inc. and the Swift project authors
6+
# Licensed under Apache License v2.0 with Runtime Library Exception
7+
#
8+
# See http://swift.org/LICENSE.txt for license information
9+
# See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
#
11+
# ----------------------------------------------------------------------------
12+
13+
import argparse
14+
import os
15+
import sys
16+
import unittest
17+
18+
from swift_build_support.arguments import type as argtype
19+
20+
21+
class ArgumentsTypeTestCase(unittest.TestCase):
22+
23+
def test_bool(self):
24+
self.assertTrue(argtype.bool("1"))
25+
self.assertTrue(argtype.bool("true"))
26+
self.assertTrue(argtype.bool("True"))
27+
28+
self.assertFalse(argtype.bool("0"))
29+
self.assertFalse(argtype.bool("false"))
30+
self.assertFalse(argtype.bool("False"))
31+
32+
self.assertRaises(argparse.ArgumentTypeError, argtype.bool, 'foobar')
33+
self.assertRaises(argparse.ArgumentTypeError, argtype.bool, 'TRUE')
34+
self.assertRaises(argparse.ArgumentTypeError, argtype.bool, 'FALSE')
35+
36+
def test_shell_split(self):
37+
self.assertEqual(
38+
argtype.shell_split("-BAR=\"foo bar\" -BAZ='foo,bar',-QUX $baz"),
39+
['-BAR=foo bar', '-BAZ=foo,bar', '-QUX', '$baz'])
40+
41+
def test_clang_compiler_version(self):
42+
self.assertEqual(
43+
argtype.clang_compiler_version('1.23.456'),
44+
("1", "23", "456"))
45+
self.assertRaises(
46+
argparse.ArgumentTypeError,
47+
argtype.clang_compiler_version,
48+
"ver1.2.3")
49+
self.assertRaises(
50+
argparse.ArgumentTypeError,
51+
argtype.clang_compiler_version,
52+
"1.beta2.3")
53+
self.assertRaises(
54+
argparse.ArgumentTypeError,
55+
argtype.clang_compiler_version,
56+
"1.2.preview3")
57+
self.assertRaises(
58+
argparse.ArgumentTypeError,
59+
argtype.clang_compiler_version,
60+
"1.2.3-rc4")
61+
self.assertRaises(
62+
argparse.ArgumentTypeError,
63+
argtype.clang_compiler_version,
64+
"1..2")
65+
66+
def test_executable(self):
67+
python = sys.executable
68+
self.assertTrue(os.path.isabs(argtype.executable(python)))
69+
70+
# On this test directory, specifying "../../build-script-impl" returns
71+
# absolute path of build-script-impl
72+
impl = os.path.join("..", "..", "build-script-impl")
73+
cwd = os.getcwd()
74+
os.chdir(os.path.dirname(__file__))
75+
self.assertTrue(os.path.isabs(argtype.executable(impl)))
76+
os.chdir(cwd)
77+
78+
self.assertRaises(
79+
argparse.ArgumentTypeError,
80+
argtype.executable, __file__) # this file is not executable
81+
self.assertRaises(
82+
argparse.ArgumentTypeError,
83+
argtype.executable, os.path.dirname(__file__))
84+
self.assertRaises(
85+
argparse.ArgumentTypeError,
86+
argtype.executable, "/bin/example-command-not-exist")
87+
self.assertRaises(
88+
argparse.ArgumentTypeError,
89+
argtype.executable, "../example-command-not-exist")

0 commit comments

Comments
 (0)