Skip to content

[build-script] Eliminate swift build support subprocess functions #2836

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

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 0 additions & 40 deletions utils/SwiftBuildSupport.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,6 @@
import configparser as ConfigParser

import os
import platform
import subprocess
import sys

sys.path.append(os.path.join(os.path.dirname(__file__), 'swift_build_support'))
Expand Down Expand Up @@ -70,44 +68,6 @@ def _get_default_source_root():
"SWIFT_BUILD_ROOT", os.path.join(SWIFT_SOURCE_ROOT, "build"))


def check_call(args, print_command=False, verbose=False, disable_sleep=False):
if disable_sleep:
if platform.system() == 'Darwin':
# Don't mutate the caller's copy of the arguments.
args = list(args)
args.insert(0, "caffeinate")

if print_command:
print(os.getcwd() + "$ " + shell.quote_command(args))
sys.stdout.flush()
try:
return subprocess.check_call(args)
except subprocess.CalledProcessError as e:
diagnostics.fatal(
"command terminated with a non-zero exit status " +
str(e.returncode) + ", aborting")
except OSError as e:
diagnostics.fatal(
"could not execute '" + shell.quote_command(args) +
"': " + e.strerror)


def check_output(args, print_command=False, verbose=False):
if print_command:
print(os.getcwd() + "$ " + shell.quote_command(args))
sys.stdout.flush()
try:
return subprocess.check_output(args)
except subprocess.CalledProcessError as e:
diagnostics.fatal(
"command terminated with a non-zero exit status " +
str(e.returncode) + ", aborting")
except OSError as e:
diagnostics.fatal(
"could not execute '" + shell.quote_command(args) +
"': " + e.strerror)


def _load_preset_files_impl(preset_file_names, substitutions={}):
config = ConfigParser.SafeConfigParser(substitutions, allow_no_value=True)
if config.read(preset_file_names) == []:
Expand Down
21 changes: 17 additions & 4 deletions utils/build-script
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@ from SwiftBuildSupport import (
HOME,
SWIFT_BUILD_ROOT,
SWIFT_SOURCE_ROOT,
check_call,
get_all_preset_names,
get_preset_options,
) # noqa (E402 module level import not at top of file)
Expand All @@ -51,6 +50,21 @@ from swift_build_support.workspace import Workspace # noqa(E402)
from swift_build_support.workspace import compute_build_subdir # noqa(E402)


def call_without_sleeping(command, dry_run=False):
"""
Execute a command during which system sleep is disabled.

By default, this ignores the state of the `shell.dry_run` flag.
"""

# Disable system sleep, if possible.
if platform.system() == 'Darwin':
# Don't mutate the caller's copy of the arguments.
command = ["caffeinate"] + list(command)

shell.call(command, dry_run=dry_run, print_command=False)


# Main entry point for the preset mode.
def main_preset():
parser = argparse.ArgumentParser(
Expand Down Expand Up @@ -124,7 +138,7 @@ def main_preset():
"using preset '" + args.preset + "', which expands to \n\n" +
shell.quote_command(build_script_args) + "\n")

check_call(build_script_args, disable_sleep=True)
call_without_sleeping(build_script_args)

return 0

Expand Down Expand Up @@ -1429,8 +1443,7 @@ details of the setups of other systems or automated environments.""")
if args.dry_run:
build_script_impl_args += ["--dry-run"]

check_call([build_script_impl] + build_script_impl_args,
disable_sleep=True)
call_without_sleeping([build_script_impl] + build_script_impl_args)

if args.symbols_package:
print('--- Creating symbols package ---')
Expand Down
15 changes: 7 additions & 8 deletions utils/profdata_merge/process.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,17 +19,16 @@
from multiprocessing import Process


# hack to import SwiftBuildSupport and swift_build_support
parent_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), '..')
sys.path.append(parent_dir)
support_dir = os.path.join(parent_dir, 'swift_build_support')
sys.path.append(support_dir)
# Allow importing swift_build_support.
sys.path.append(os.path.join(os.path.dirname(os.path.abspath(__file__)), '..',
'swift_build_support'))
from swift_build_support import shell # noqa (E402)
from swift_build_support.toolchain import host_toolchain # noqa (E402)
from SwiftBuildSupport import check_output, check_call # noqa (E402)

toolchain = host_toolchain()
LLVM_PROFDATA_PATH = toolchain.llvm_profdata
_profdata_help = check_output([LLVM_PROFDATA_PATH, 'merge', '-help'])
_profdata_help = shell.capture([LLVM_PROFDATA_PATH, 'merge', '-help'],
print_command=False)
LLVM_PROFDATA_SUPPORTS_SPARSE = 'sparse' in _profdata_help


Expand Down Expand Up @@ -65,7 +64,7 @@ def merge_file_buffer(self):
llvm_cmd.append("-sparse")
llvm_cmd += cleaned_files
self.report(llvm_cmd)
ret = check_call(llvm_cmd)
ret = shell.call(llvm_cmd, print_command=False)
if ret != 0:
self.report("llvm profdata command failed -- Exited with code %d"
% ret, level=logging.ERROR)
Expand Down
9 changes: 6 additions & 3 deletions utils/recursive-lipo
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,11 @@ import filecmp
import os
import os.path
import shutil
import sys

from SwiftBuildSupport import check_call
sys.path.append(os.path.join(os.path.dirname(__file__), 'swift_build_support'))

from swift_build_support import shell # noqa (E402)


def merge_file_lists(src_root_dirs, skip_files, skip_subpaths):
Expand Down Expand Up @@ -89,8 +92,8 @@ def merge_lipo_files(src_root_dirs, file_list, copy_verbatim_subpaths,
print("-- Running lipo %s to %s" % (file_paths, dest_path))
else:
print("-- Running lipo %s" % dest_path)
check_call(lipo_cmd + ["-output", dest_path] + file_paths,
print_command=verbose, verbose=verbose)
shell.call(lipo_cmd + ["-output", dest_path] + file_paths,
print_command=verbose)
else:
# Multiple instances are different, and they're not executable.
print(
Expand Down
64 changes: 53 additions & 11 deletions utils/swift_build_support/swift_build_support/shell.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,9 +59,17 @@ def _print_command(dry_run, command, env=None, prompt="+ "):
file.flush()


def call(command, stderr=None, env=None, dry_run=None):
def call(command, stderr=None, env=None, dry_run=None, print_command=True):
"""
call(command, ...) -> str

Execute the given command.

This function will raise an exception on any command failure.
"""
dry_run = _coerce_dry_run(dry_run)
_print_command(dry_run, command, env=env)
if dry_run or print_command:
_print_command(dry_run, command, env=env)
if dry_run:
return
_env = None
Expand All @@ -80,40 +88,74 @@ def call(command, stderr=None, env=None, dry_run=None):
"': " + e.strerror)


def capture(command, stderr=None, env=None, dry_run=None, print_command=True):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For this method, print_command=False is relevant IMO.
BTW, Could you change print_command to echo or trace? it's easier to type.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For this method, print_command=False is relevant IMO.

Did you mean irrelevant? Or that the default should be different?

BTW, Could you change print_command to echo or trace? it's easier to type.

I agree, I would prefer a shorter name too but I was preserving the existing name carried over from SwiftBuildSupport. If @gribozavr has no objections I will switch to echo.

Copy link
Member

@rintaro rintaro Jun 2, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, the default for print_command should be False only for capture method.
I know, it's inconsistent with other methods,
but we rarely want to "trace" side-effect less query command.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree that False makes more sense as a default, but didn't want the methods to be inconsistent. There is also an argument to be made that we generally shouldn't usually be shelling out to query things, and if we are it might be just as useful for logging purposes to see what commands contributed to the build script behavior.

As the module stands I think keeping the methods consistent at the cost of slightly more verbose clients is the right tradeoff.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1 for echo. (@echo off anyone?)

And I also agree that even though capture would not usually be echoed, consistency is important.

"""
capture(command, ...) -> str

Execute the given command and return the standard output.

This function will raise an exception on any command failure.
"""
dry_run = _coerce_dry_run(dry_run)
if dry_run or print_command:
_print_command(dry_run, command, env=env)
if dry_run:
return
_env = None
if env is not None:
_env = dict(os.environ)
_env.update(env)
try:
return subprocess.check_output(command, env=_env, stderr=stderr)
except subprocess.CalledProcessError as e:
diagnostics.fatal(
"command terminated with a non-zero exit status " +
str(e.returncode) + ", aborting")
except OSError as e:
diagnostics.fatal(
"could not execute '" + quote_command(command) +
"': " + e.strerror)


@contextmanager
def pushd(path, dry_run=None):
def pushd(path, dry_run=None, print_command=True):
dry_run = _coerce_dry_run(dry_run)
old_dir = os.getcwd()
_print_command(dry_run, ["pushd", path])
if dry_run or print_command:
_print_command(dry_run, ["pushd", path])
if not dry_run:
os.chdir(path)
yield
_print_command(dry_run, ["popd"])
if dry_run or print_command:
_print_command(dry_run, ["popd"])
if not dry_run:
os.chdir(old_dir)


def makedirs(path, dry_run=None):
def makedirs(path, dry_run=None, print_command=True):
dry_run = _coerce_dry_run(dry_run)
_print_command(dry_run, ['mkdir', '-p', path])
if dry_run or print_command:
_print_command(dry_run, ['mkdir', '-p', path])
if dry_run:
return
if not os.path.isdir(path):
os.makedirs(path)


def rmtree(path, dry_run=None):
def rmtree(path, dry_run=None, print_command=True):
dry_run = _coerce_dry_run(dry_run)
_print_command(dry_run, ['rm', '-rf', path])
if dry_run or print_command:
_print_command(dry_run, ['rm', '-rf', path])
if dry_run:
return
if os.path.exists(path):
shutil.rmtree(path)


def copytree(src, dest, dry_run=None):
def copytree(src, dest, dry_run=None, print_command=True):
dry_run = _coerce_dry_run(dry_run)
_print_command(dry_run, ['cp', '-r', src, dest])
if dry_run or print_command:
_print_command(dry_run, ['cp', '-r', src, dest])
if dry_run:
return
shutil.copytree(src, dest)
6 changes: 6 additions & 0 deletions utils/swift_build_support/tests/test_shell.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,12 @@ def test_call(self):
+ cp {foo_file} {bar_file}
'''.format(foo_file=foo_file, bar_file=bar_file))

def test_capture(self):
self.assertEqual(shell.capture(["echo", "hi"]), "hi\n")

with self.assertRaises(SystemExit):
shell.capture(["false"])

def test_rmtree(self):
shell.dry_run = False
path = os.path.join(self.tmpdir, 'foo', 'bar')
Expand Down
1 change: 0 additions & 1 deletion utils/swift_build_support/tests/test_tar.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@

import os
import platform
import subprocess
import sys
import tempfile
import unittest
Expand Down
31 changes: 16 additions & 15 deletions utils/update-checkout
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,6 @@ sys.path.append(os.path.dirname(__file__))

from SwiftBuildSupport import (
SWIFT_SOURCE_ROOT,
check_call,
check_output,
) # noqa (E402 module level import not at top of file)

sys.path.append(os.path.join(os.path.dirname(__file__), 'swift_build_support'))
Expand Down Expand Up @@ -94,21 +92,23 @@ def update_working_copy(repo_path, branch):
return

print("--- Updating '" + repo_path + "' ---")
with shell.pushd(repo_path, dry_run=False):
with shell.pushd(repo_path, dry_run=False, print_command=False):
if branch:
status = check_output(['git', 'status', '--porcelain'])
status = shell.capture(['git', 'status', '--porcelain'],
print_command=False)
if status:
print("Please, commit your changes.")
print(status)
exit(1)
check_call(['git', 'checkout', branch])
shell.call(['git', 'checkout', branch], print_command=False)

# Prior to Git 2.6, this is the way to do a "git pull
# --rebase" that respects rebase.autostash. See
# http://stackoverflow.com/a/30209750/125349
check_call(["git", "fetch"])
check_call(["git", "rebase", "FETCH_HEAD"])
check_call(["git", "submodule", "update", "--recursive"])
shell.call(["git", "fetch"], print_command=False)
shell.call(["git", "rebase", "FETCH_HEAD"], print_command=False)
shell.call(["git", "submodule", "update", "--recursive"],
print_command=False)


def obtain_additional_swift_sources(
Expand All @@ -117,19 +117,20 @@ def obtain_additional_swift_sources(
if dir_name in skip_repositories:
print("--- Skipping '" + dir_name + "' ---")
continue
with shell.pushd(SWIFT_SOURCE_ROOT, dry_run=False):
with shell.pushd(SWIFT_SOURCE_ROOT, dry_run=False,
print_command=False):
if not os.path.isdir(os.path.join(dir_name, ".git")):
print("--- Cloning '" + dir_name + "' ---")
if with_ssh is True:
remote = "[email protected]:" + repo + '.git'
else:
remote = "https://github.com/" + repo + '.git'
if skip_history:
check_call(['git', 'clone', '--recursive', '--depth', '1',
remote, dir_name])
shell.call(['git', 'clone', '--recursive', '--depth', '1',
remote, dir_name], print_command=False)
else:
check_call(['git', 'clone', '--recursive', remote,
dir_name])
shell.call(['git', 'clone', '--recursive', remote,
dir_name], print_command=False)
if branch:
if branch == "master" or branch == "stable":
repo_branch = MASTER_BRANCHES[dir_name]
Expand All @@ -142,9 +143,9 @@ def obtain_additional_swift_sources(
repo_branch = branch
src_path = SWIFT_SOURCE_ROOT + "/" + dir_name + "/" + \
".git"
check_call(['git', '--git-dir', src_path, '--work-tree',
shell.call(['git', '--git-dir', src_path, '--work-tree',
os.path.join(SWIFT_SOURCE_ROOT, dir_name),
'checkout', repo_branch])
'checkout', repo_branch], print_command=False)


def main():
Expand Down