From bcb8a06ef7929417f09ed5055692d8afc59b3c70 Mon Sep 17 00:00:00 2001 From: "NODA, Kai" Date: Sat, 23 Jun 2018 16:51:19 +0800 Subject: [PATCH 1/2] bootstrap: write texts to a .tmp file first for atomicity If you are using a hard-linked file as your config.toml, this change will affect the way other instances of the file is modified. The original version would modify all other instances whereas the new version will leave others unchanged, reducing the ref count by one. Signed-off-by: NODA, Kai --- src/bootstrap/bootstrap.py | 14 +++++++++++--- src/bootstrap/configure.py | 4 ++-- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/src/bootstrap/bootstrap.py b/src/bootstrap/bootstrap.py index 512d4d8c5b792..d1aa32236aeee 100644 --- a/src/bootstrap/bootstrap.py +++ b/src/bootstrap/bootstrap.py @@ -303,6 +303,14 @@ def default_build_triple(): return "{}-{}".format(cputype, ostype) +@contextlib.contextmanager +def output(filepath): + tmp = filepath + '.tmp' + with open(tmp, 'w') as f: + yield f + os.rename(tmp, filepath) + + class RustBuild(object): """Provide all the methods required to build Rust""" def __init__(self): @@ -346,7 +354,7 @@ def download_stage0(self): self._download_stage0_helper(filename, "rustc") self.fix_executable("{}/bin/rustc".format(self.bin_root())) self.fix_executable("{}/bin/rustdoc".format(self.bin_root())) - with open(self.rustc_stamp(), 'w') as rust_stamp: + with output(self.rustc_stamp()) as rust_stamp: rust_stamp.write(self.date) # This is required so that we don't mix incompatible MinGW @@ -363,7 +371,7 @@ def download_stage0(self): filename = "cargo-{}-{}.tar.gz".format(cargo_channel, self.build) self._download_stage0_helper(filename, "cargo") self.fix_executable("{}/bin/cargo".format(self.bin_root())) - with open(self.cargo_stamp(), 'w') as cargo_stamp: + with output(self.cargo_stamp()) as cargo_stamp: cargo_stamp.write(self.date) def _download_stage0_helper(self, filename, pattern): @@ -776,7 +784,7 @@ def bootstrap(help_triggered): if build.use_vendored_sources: if not os.path.exists('.cargo'): os.makedirs('.cargo') - with open('.cargo/config', 'w') as cargo_config: + with output('.cargo/config') as cargo_config: cargo_config.write(""" [source.crates-io] replace-with = 'vendored-sources' diff --git a/src/bootstrap/configure.py b/src/bootstrap/configure.py index 80fa96509bd87..9fdba044f4be3 100755 --- a/src/bootstrap/configure.py +++ b/src/bootstrap/configure.py @@ -432,7 +432,7 @@ def configure_section(lines, config): # order that we read it in. p("") p("writing `config.toml` in current directory") -with open('config.toml', 'w') as f: +with bootstrap.output('config.toml') as f: for section in section_order: if section == 'target': for target in targets: @@ -442,7 +442,7 @@ def configure_section(lines, config): for line in sections[section]: f.write(line + "\n") -with open('Makefile', 'w') as f: +with bootstrap.output('Makefile') as f: contents = os.path.join(rust_dir, 'src', 'bootstrap', 'mk', 'Makefile.in') contents = open(contents).read() contents = contents.replace("$(CFG_SRC_DIR)", rust_dir + '/') From 97d0bc3f044a2f105ec760f78da800a1e44c8f05 Mon Sep 17 00:00:00 2001 From: "NODA, Kai" Date: Wed, 27 Jun 2018 03:23:14 +0800 Subject: [PATCH 2/2] bootstrap: our best to achieve atomic rename on Win32 This is a tricky operation to implement on Win32; see https://ci.appveyor.com/project/nodakai/python-win-behavior Signed-off-by: NODA, Kai --- src/bootstrap/bootstrap.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/bootstrap/bootstrap.py b/src/bootstrap/bootstrap.py index d1aa32236aeee..71c1c61e3d97e 100644 --- a/src/bootstrap/bootstrap.py +++ b/src/bootstrap/bootstrap.py @@ -308,7 +308,12 @@ def output(filepath): tmp = filepath + '.tmp' with open(tmp, 'w') as f: yield f - os.rename(tmp, filepath) + try: + os.remove(filepath) # PermissionError/OSError on Win32 if in use + os.rename(tmp, filepath) + except OSError: + shutil.copy2(tmp, filepath) + os.remove(tmp) class RustBuild(object):