Skip to content

Commit b1109c6

Browse files
committed
[build-script] Provide centralized command line and file system interface for build-script.
1 parent 3661a0e commit b1109c6

File tree

5 files changed

+286
-6
lines changed

5 files changed

+286
-6
lines changed

utils/build-script

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ import pipes
1818
import platform
1919
import re
2020
import shlex
21-
import shutil
2221
import sys
2322

2423
# FIXME: Instead of modifying the system path in order to enable imports from
@@ -31,7 +30,6 @@ from SwiftBuildSupport import (
3130
HOME,
3231
SWIFT_BUILD_ROOT,
3332
SWIFT_SOURCE_ROOT,
34-
WorkingDirectory,
3533
check_call,
3634
get_all_preset_names,
3735
get_preset_options,
@@ -45,6 +43,7 @@ sys.path.append(os.path.join(os.path.dirname(__file__), 'swift_build_support'))
4543
from swift_build_support.toolchain import host_toolchain # noqa (E402)
4644
import swift_build_support.debug # noqa (E402)
4745
from swift_build_support import migration # noqa (E402)
46+
from swift_build_support import shell # noqa (E402)
4847
import swift_build_support.tar # noqa (E402)
4948
import swift_build_support.targets # noqa (E402)
5049
from swift_build_support.cmake import CMake # noqa (E402)
@@ -1200,8 +1199,9 @@ details of the setups of other systems or automated environments.""")
12001199
source_root=SWIFT_SOURCE_ROOT,
12011200
build_root=os.path.join(SWIFT_BUILD_ROOT, args.build_subdir))
12021201

1203-
if args.clean and os.path.isdir(workspace.build_root):
1204-
shutil.rmtree(workspace.build_root)
1202+
# Clean build_dir if requested.
1203+
if args.clean:
1204+
shell.rmtree(workspace.build_root)
12051205

12061206
cmake = CMake(args=args,
12071207
toolchain=toolchain)
@@ -1411,7 +1411,7 @@ details of the setups of other systems or automated environments.""")
14111411
# it is archiving. To stay safe, we change working directories, then
14121412
# run `tar` without the leading '/' (we remove it ourselves to keep
14131413
# `tar` from emitting a warning).
1414-
with WorkingDirectory(args.install_symroot):
1414+
with shell.pushd(args.install_symroot):
14151415
swift_build_support.tar.tar(source=prefix.lstrip('/'),
14161416
destination=args.symbols_package)
14171417

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
# swift_build_support/shell.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+
Centralized command line and file system interface for the build script.
13+
"""
14+
# ----------------------------------------------------------------------------
15+
16+
from __future__ import print_function
17+
18+
import os
19+
import shutil
20+
import subprocess
21+
import sys
22+
import pipes
23+
from contextlib import contextmanager
24+
25+
dry_run = False
26+
27+
28+
def _quote(arg):
29+
return pipes.quote(str(arg))
30+
31+
32+
def _coerce_dry_run(dry_run_override):
33+
if dry_run_override is None:
34+
return dry_run
35+
else:
36+
return dry_run_override
37+
38+
39+
def _print_command(dry_run, command, env=None, prompt="+ "):
40+
output = []
41+
if env is not None:
42+
output += ['env'] + [_quote("%s=%s" % (k, v)) for k, v in env]
43+
output += [_quote(arg) for arg in command]
44+
file = None
45+
if not dry_run:
46+
file = sys.stderr
47+
print(prompt + ' '.join(output), file=file)
48+
49+
50+
def call(command, stderr=None, env=None, dry_run=None):
51+
dry_run = _coerce_dry_run(dry_run)
52+
_print_command(dry_run, command, env=env)
53+
if dry_run:
54+
return
55+
_env = None
56+
if env is not None:
57+
_env = dict(os.environ)
58+
_env.update(env)
59+
subprocess.check_call(command, env=_env, stderr=stderr)
60+
61+
62+
@contextmanager
63+
def pushd(path, dry_run=None):
64+
dry_run = _coerce_dry_run(dry_run)
65+
old_dir = os.getcwd()
66+
_print_command(dry_run, ["pushd", path])
67+
if not dry_run:
68+
os.chdir(path)
69+
yield
70+
_print_command(dry_run, ["popd"])
71+
if not dry_run:
72+
os.chdir(old_dir)
73+
74+
75+
def makedirs(path, dry_run=None):
76+
dry_run = _coerce_dry_run(dry_run)
77+
_print_command(dry_run, ['mkdir', '-p', path])
78+
if dry_run:
79+
return
80+
if not os.path.isdir(path):
81+
os.makedirs(path)
82+
83+
84+
def rmtree(path, dry_run=None):
85+
dry_run = _coerce_dry_run(dry_run)
86+
_print_command(dry_run, ['rm', '-rf', path])
87+
if dry_run:
88+
return
89+
if os.path.exists(path):
90+
shutil.rmtree(path)
91+
92+
93+
def copytree(src, dest, dry_run=None):
94+
dry_run = _coerce_dry_run(dry_run)
95+
_print_command(dry_run, ['cp', '-r', src, dest])
96+
if dry_run:
97+
return
98+
shutil.copytree(src, dest)

utils/swift_build_support/swift_build_support/tar.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,13 @@
88
# See http://swift.org/LICENSE.txt for license information
99
# See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
1010

11+
from __future__ import absolute_import
12+
1113
import platform
1214
import subprocess
1315

16+
from . import shell
17+
1418

1519
def tar(source, destination):
1620
"""
@@ -28,4 +32,4 @@ def tar(source, destination):
2832
# Capture stderr output such as 'tar: Failed to open ...'. We'll detect
2933
# these cases using the exit code, which should cause 'check_call' to
3034
# raise.
31-
subprocess.check_call(args + [source], stderr=subprocess.PIPE)
35+
shell.call(args + [source], stderr=subprocess.PIPE)
Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
# tests/test_shell.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+
import unittest
13+
import sys
14+
import os
15+
import os.path
16+
import shutil
17+
import tempfile
18+
try:
19+
# py2
20+
from StringIO import StringIO
21+
except ImportError:
22+
# py3
23+
from io import StringIO
24+
25+
from swift_build_support import shell
26+
27+
28+
class ShellTestCase(unittest.TestCase):
29+
30+
def setUp(self):
31+
self.tmpdir = os.path.realpath(tempfile.mkdtemp())
32+
self._orig_stdout = sys.stdout
33+
self._orig_stderr = sys.stderr
34+
self.stdout = StringIO()
35+
self.stderr = StringIO()
36+
sys.stdout = self.stdout
37+
sys.stderr = self.stderr
38+
39+
def tearDown(self):
40+
sys.stdout = self._orig_stdout
41+
sys.stderr = self._orig_stderr
42+
if os.path.exists(self.tmpdir):
43+
shutil.rmtree(self.tmpdir)
44+
45+
def test_call(self):
46+
shell.dry_run = False
47+
foo_file = os.path.join(self.tmpdir, 'foo.txt')
48+
bar_file = os.path.join(self.tmpdir, 'bar.txt')
49+
50+
with open(foo_file, 'w') as f:
51+
f.write("Hello Swift")
52+
53+
shell.call(['cp', foo_file, bar_file])
54+
55+
with open(bar_file, 'r') as f:
56+
self.assertEqual(f.read(), "Hello Swift")
57+
58+
self.assertEqual(self.stdout.getvalue(), "")
59+
self.assertEqual(self.stderr.getvalue(), '''\
60+
+ cp {foo_file} {bar_file}
61+
'''.format(foo_file=foo_file, bar_file=bar_file))
62+
63+
def test_rmtree(self):
64+
shell.dry_run = False
65+
path = os.path.join(self.tmpdir, 'foo', 'bar')
66+
shell.makedirs(path)
67+
68+
self.assertTrue(os.path.isdir(path))
69+
70+
shell.rmtree(os.path.join(path))
71+
self.assertFalse(
72+
os.path.exists(os.path.join(path)))
73+
self.assertTrue(
74+
os.path.exists(os.path.join(self.tmpdir, 'foo')))
75+
76+
self.assertEqual(self.stdout.getvalue(), "")
77+
self.assertEqual(self.stderr.getvalue(), '''\
78+
+ mkdir -p {path}
79+
+ rm -rf {path}
80+
'''.format(path=path))
81+
82+
def test_pushd(self):
83+
shell.dry_run = False
84+
basedir = os.getcwd()
85+
86+
with shell.pushd(self.tmpdir):
87+
self.assertEqual(os.getcwd(), self.tmpdir)
88+
self.assertEqual(os.getcwd(), basedir)
89+
90+
# pushd inside pushd
91+
with shell.pushd(self.tmpdir):
92+
self.assertEqual(os.getcwd(), self.tmpdir)
93+
shell.makedirs('foo')
94+
with shell.pushd('foo'):
95+
self.assertEqual(os.getcwd(),
96+
os.path.join(self.tmpdir, 'foo'))
97+
self.assertEqual(os.getcwd(), self.tmpdir)
98+
self.assertEqual(os.getcwd(), basedir)
99+
100+
# cd inside pushd
101+
with shell.pushd(self.tmpdir):
102+
os.chdir('foo')
103+
self.assertEqual(os.getcwd(), os.path.join(self.tmpdir, 'foo'))
104+
os.chdir('..')
105+
self.assertEqual(os.getcwd(), self.tmpdir)
106+
shell.rmtree('foo')
107+
self.assertEqual(os.getcwd(), basedir)
108+
109+
self.assertEqual(self.stdout.getvalue(), "")
110+
self.assertEqual(self.stderr.getvalue(), '''\
111+
+ pushd {tmpdir}
112+
+ popd
113+
+ pushd {tmpdir}
114+
+ mkdir -p foo
115+
+ pushd foo
116+
+ popd
117+
+ popd
118+
+ pushd {tmpdir}
119+
+ rm -rf foo
120+
+ popd
121+
'''.format(tmpdir=self.tmpdir))
122+
123+
def test_dry_run(self):
124+
shell.dry_run = True
125+
126+
basedir = os.getcwd()
127+
foobar_dir = os.path.join(self.tmpdir, 'foo', 'bar')
128+
129+
shell.makedirs(foobar_dir)
130+
self.assertFalse(os.path.exists(os.path.join(self.tmpdir, 'foo')))
131+
self.assertFalse(os.path.exists(foobar_dir))
132+
133+
with shell.pushd(foobar_dir):
134+
self.assertEqual(os.getcwd(), basedir)
135+
shell.call(['touch', 'testfile'])
136+
self.assertFalse(os.path.exists(
137+
os.path.join(foobar_dir, 'testfile')))
138+
139+
self.assertEqual(os.getcwd(), basedir)
140+
141+
shell.rmtree(self.tmpdir)
142+
self.assertTrue(os.path.exists(self.tmpdir))
143+
144+
self.assertEqual(self.stdout.getvalue(), '''\
145+
+ mkdir -p {foobar_dir}
146+
+ pushd {foobar_dir}
147+
+ touch testfile
148+
+ popd
149+
+ rm -rf {tmpdir}
150+
'''.format(foobar_dir=foobar_dir, tmpdir=self.tmpdir))
151+
self.assertEqual(self.stderr.getvalue(), "")
152+
self.dry_run = False

utils/swift_build_support/tests/test_tar.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,21 +9,47 @@
99
# See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
1010

1111
import os
12+
import sys
1213
import subprocess
1314
import tempfile
1415
import unittest
16+
import platform
17+
try:
18+
from StringIO import StringIO
19+
except ImportError:
20+
from io import StringIO
1521

1622
from swift_build_support.tar import tar
1723

1824

1925
class TarTestCase(unittest.TestCase):
26+
def setUp(self):
27+
self._orig_stdout = sys.stdout
28+
self._orig_stderr = sys.stderr
29+
self.stdout = StringIO()
30+
self.stderr = StringIO()
31+
sys.stdout = self.stdout
32+
sys.stderr = self.stderr
33+
34+
def tearDown(self):
35+
sys.stdout = self._orig_stdout
36+
sys.stderr = self._orig_stderr
2037

2138
def test_tar_this_file_succeeds(self):
2239
# `tar` complains about absolute paths, so use a relative path here.
2340
source = os.path.relpath(__file__)
2441
_, destination = tempfile.mkstemp()
2542
tar(source=source, destination=destination)
2643

44+
if platform.system() == "Darwin":
45+
expect = "+ tar -c -z -f {dest} {source}\n"
46+
else:
47+
expect = "+ tar -c -z -f {dest} --owner=0 --group=0 {source}\n"
48+
49+
self.assertEqual(self.stdout.getvalue(), "")
50+
self.assertEqual(self.stderr.getvalue(),
51+
expect.format(dest=destination, source=source))
52+
2753
def test_tar_nonexistent_file_raises(self):
2854
with self.assertRaises(subprocess.CalledProcessError):
2955
tar(source='/path/to/something/that/surely/doesnt/exist',

0 commit comments

Comments
 (0)