@@ -458,23 +458,51 @@ def unpack_component(download_info):
458458 verbose = download_info .verbose ,
459459 )
460460
461- class RustBuild ( object ) :
462- """Provide all the methods required to build Rust """
461+ class FakeArgs :
462+ """Used for unit tests to avoid updating all call sites """
463463 def __init__ (self ):
464- self .checksums_sha256 = {}
465- self .stage0_compiler = None
466- self .download_url = ''
467464 self .build = ''
468465 self .build_dir = ''
469466 self .clean = False
470- self .config_toml = ''
471- self .rust_root = ''
472- self .use_locked_deps = False
473- self .use_vendored_sources = False
474467 self .verbose = False
468+ self .json_output = False
469+ self .color = 'auto'
470+ self .warnings = 'default'
471+
472+ class RustBuild (object ):
473+ """Provide all the methods required to build Rust"""
474+ def __init__ (self , config_toml = "" , args = FakeArgs ()):
475475 self .git_version = None
476476 self .nix_deps_dir = None
477477 self ._should_fix_bins_and_dylibs = None
478+ self .rust_root = os .path .abspath (os .path .join (__file__ , '../../..' ))
479+
480+ self .config_toml = config_toml
481+
482+ self .clean = args .clean
483+ self .json_output = args .json_output
484+ self .verbose = args .verbose
485+ self .color = args .color
486+ self .warnings = args .warnings
487+
488+ config_verbose_count = self .get_toml ('verbose' , 'build' )
489+ if config_verbose_count is not None :
490+ self .verbose = max (self .verbose , int (config_verbose_count ))
491+
492+ self .use_vendored_sources = self .get_toml ('vendor' , 'build' ) == 'true'
493+ self .use_locked_deps = self .get_toml ('locked-deps' , 'build' ) == 'true'
494+
495+ build_dir = args .build_dir or self .get_toml ('build-dir' , 'build' ) or 'build'
496+ self .build_dir = os .path .abspath (build_dir )
497+
498+ with open (os .path .join (self .rust_root , "src" , "stage0.json" )) as f :
499+ data = json .load (f )
500+ self .checksums_sha256 = data ["checksums_sha256" ]
501+ self .stage0_compiler = Stage0Toolchain (data ["compiler" ])
502+ self .download_url = os .getenv ("RUSTUP_DIST_SERVER" ) or data ["config" ]["dist_server" ]
503+
504+ self .build = args .build or self .build_triple ()
505+
478506
479507 def download_toolchain (self ):
480508 """Fetch the build system for Rust, written in Rust
@@ -704,9 +732,10 @@ def rustc_stamp(self):
704732 """Return the path for .rustc-stamp at the given stage
705733
706734 >>> rb = RustBuild()
735+ >>> rb.build = "host"
707736 >>> rb.build_dir = "build"
708- >>> rb.rustc_stamp() == os.path.join("build", "stage0", ".rustc-stamp")
709- True
737+ >>> expected = os.path.join("build", "host ", "stage0", ".rustc-stamp")
738+ >>> assert rb.rustc_stamp() == expected, rb.rustc_stamp()
710739 """
711740 return os .path .join (self .bin_root (), '.rustc-stamp' )
712741
@@ -721,15 +750,9 @@ def bin_root(self):
721750 """Return the binary root directory for the given stage
722751
723752 >>> rb = RustBuild()
724- >>> rb.build_dir = "build"
725- >>> rb.bin_root() == os.path.join("build", "stage0")
726- True
727-
728- When the 'build' property is given should be a nested directory:
729-
730753 >>> rb.build = "devel"
731- >>> rb.bin_root() == os.path.join("build", "devel", "stage0")
732- True
754+ >>> expected = os.path.abspath(os.path. join("build", "devel", "stage0") )
755+ >>> assert rb.bin_root() == expected, rb.bin_root()
733756 """
734757 subdir = "stage0"
735758 return os .path .join (self .build_dir , self .build , subdir )
@@ -761,9 +784,12 @@ def get_toml(self, key, section=None):
761784 >>> rb.get_toml("key1")
762785 'true'
763786 """
787+ return RustBuild .get_toml_static (self .config_toml , key , section )
764788
789+ @staticmethod
790+ def get_toml_static (config_toml , key , section = None ):
765791 cur_section = None
766- for line in self . config_toml .splitlines ():
792+ for line in config_toml .splitlines ():
767793 section_match = re .match (r'^\s*\[(.*)\]\s*$' , line )
768794 if section_match is not None :
769795 cur_section = section_match .group (1 )
@@ -772,7 +798,7 @@ def get_toml(self, key, section=None):
772798 if match is not None :
773799 value = match .group (1 )
774800 if section is None or section == cur_section :
775- return self .get_string (value ) or value .strip ()
801+ return RustBuild .get_string (value ) or value .strip ()
776802 return None
777803
778804 def cargo (self ):
@@ -835,13 +861,23 @@ def bootstrap_binary(self):
835861 """
836862 return os .path .join (self .build_dir , "bootstrap" , "debug" , "bootstrap" )
837863
838- def build_bootstrap (self , color , warnings , verbose_count ):
864+ def build_bootstrap (self ):
839865 """Build bootstrap"""
840866 env = os .environ .copy ()
841867 if "GITHUB_ACTIONS" in env :
842868 print ("::group::Building bootstrap" )
843869 else :
844870 print ("Building bootstrap" , file = sys .stderr )
871+
872+ args = self .build_bootstrap_cmd (env )
873+ # Run this from the source directory so cargo finds .cargo/config
874+ run (args , env = env , verbose = self .verbose , cwd = self .rust_root )
875+
876+ if "GITHUB_ACTIONS" in env :
877+ print ("::endgroup::" )
878+
879+ def build_bootstrap_cmd (self , env ):
880+ """For tests."""
845881 build_dir = os .path .join (self .build_dir , "bootstrap" )
846882 if self .clean and os .path .exists (build_dir ):
847883 shutil .rmtree (build_dir )
@@ -894,10 +930,10 @@ def build_bootstrap(self, color, warnings, verbose_count):
894930 if target_linker is not None :
895931 env ["RUSTFLAGS" ] += " -C linker=" + target_linker
896932 env ["RUSTFLAGS" ] += " -Wrust_2018_idioms -Wunused_lifetimes"
897- if warnings == "default" :
933+ if self . warnings == "default" :
898934 deny_warnings = self .get_toml ("deny-warnings" , "rust" ) != "false"
899935 else :
900- deny_warnings = warnings == "deny"
936+ deny_warnings = self . warnings == "deny"
901937 if deny_warnings :
902938 env ["RUSTFLAGS" ] += " -Dwarnings"
903939
@@ -908,7 +944,7 @@ def build_bootstrap(self, color, warnings, verbose_count):
908944 self .cargo ()))
909945 args = [self .cargo (), "build" , "--manifest-path" ,
910946 os .path .join (self .rust_root , "src/bootstrap/Cargo.toml" )]
911- args .extend ("--verbose" for _ in range (verbose_count ))
947+ args .extend ("--verbose" for _ in range (self . verbose ))
912948 if self .use_locked_deps :
913949 args .append ("--locked" )
914950 if self .use_vendored_sources :
@@ -918,20 +954,16 @@ def build_bootstrap(self, color, warnings, verbose_count):
918954 args .append ("build-metrics" )
919955 if self .json_output :
920956 args .append ("--message-format=json" )
921- if color == "always" :
957+ if self . color == "always" :
922958 args .append ("--color=always" )
923- elif color == "never" :
959+ elif self . color == "never" :
924960 args .append ("--color=never" )
925961 try :
926962 args += env ["CARGOFLAGS" ].split ()
927963 except KeyError :
928964 pass
929965
930- # Run this from the source directory so cargo finds .cargo/config
931- run (args , env = env , verbose = self .verbose , cwd = self .rust_root )
932-
933- if "GITHUB_ACTIONS" in env :
934- print ("::endgroup::" )
966+ return args
935967
936968 def build_triple (self ):
937969 """Build triple as in LLVM
@@ -981,7 +1013,7 @@ def check_vendored_status(self):
9811013 if os .path .exists (cargo_dir ):
9821014 shutil .rmtree (cargo_dir )
9831015
984- def parse_args ():
1016+ def parse_args (args ):
9851017 """Parse the command line arguments that the python script needs."""
9861018 parser = argparse .ArgumentParser (add_help = False )
9871019 parser .add_argument ('-h' , '--help' , action = 'store_true' )
@@ -994,16 +1026,11 @@ def parse_args():
9941026 parser .add_argument ('--warnings' , choices = ['deny' , 'warn' , 'default' ], default = 'default' )
9951027 parser .add_argument ('-v' , '--verbose' , action = 'count' , default = 0 )
9961028
997- return parser .parse_known_args (sys . argv )[0 ]
1029+ return parser .parse_known_args (args )[0 ]
9981030
9991031def bootstrap (args ):
10001032 """Configure, fetch, build and run the initial bootstrap"""
1001- # Configure initial bootstrap
1002- build = RustBuild ()
1003- build .rust_root = os .path .abspath (os .path .join (__file__ , '../../..' ))
1004- build .verbose = args .verbose != 0
1005- build .clean = args .clean
1006- build .json_output = args .json_output
1033+ rust_root = os .path .abspath (os .path .join (__file__ , '../../..' ))
10071034
10081035 # Read from `--config`, then `RUST_BOOTSTRAP_CONFIG`, then `./config.toml`,
10091036 # then `config.toml` in the root directory.
@@ -1012,52 +1039,37 @@ def bootstrap(args):
10121039 if using_default_path :
10131040 toml_path = 'config.toml'
10141041 if not os .path .exists (toml_path ):
1015- toml_path = os .path .join (build . rust_root , toml_path )
1042+ toml_path = os .path .join (rust_root , toml_path )
10161043
10171044 # Give a hard error if `--config` or `RUST_BOOTSTRAP_CONFIG` are set to a missing path,
10181045 # but not if `config.toml` hasn't been created.
10191046 if not using_default_path or os .path .exists (toml_path ):
10201047 with open (toml_path ) as config :
1021- build .config_toml = config .read ()
1048+ config_toml = config .read ()
1049+ else :
1050+ config_toml = ''
10221051
1023- profile = build . get_toml ( 'profile' )
1052+ profile = RustBuild . get_toml_static ( config_toml , 'profile' )
10241053 if profile is not None :
10251054 include_file = 'config.{}.toml' .format (profile )
1026- include_dir = os .path .join (build . rust_root , 'src' , 'bootstrap' , 'defaults' )
1055+ include_dir = os .path .join (rust_root , 'src' , 'bootstrap' , 'defaults' )
10271056 include_path = os .path .join (include_dir , include_file )
1028- # HACK: This works because `build .get_toml()` returns the first match it finds for a
1057+ # HACK: This works because `self .get_toml()` returns the first match it finds for a
10291058 # specific key, so appending our defaults at the end allows the user to override them
10301059 with open (include_path ) as included_toml :
1031- build .config_toml += os .linesep + included_toml .read ()
1032-
1033- verbose_count = args .verbose
1034- config_verbose_count = build .get_toml ('verbose' , 'build' )
1035- if config_verbose_count is not None :
1036- verbose_count = max (args .verbose , int (config_verbose_count ))
1037-
1038- build .use_vendored_sources = build .get_toml ('vendor' , 'build' ) == 'true'
1039- build .use_locked_deps = build .get_toml ('locked-deps' , 'build' ) == 'true'
1060+ config_toml += os .linesep + included_toml .read ()
10401061
1062+ # Configure initial bootstrap
1063+ build = RustBuild (config_toml , args )
10411064 build .check_vendored_status ()
10421065
1043- build_dir = args .build_dir or build .get_toml ('build-dir' , 'build' ) or 'build'
1044- build .build_dir = os .path .abspath (build_dir )
1045-
1046- with open (os .path .join (build .rust_root , "src" , "stage0.json" )) as f :
1047- data = json .load (f )
1048- build .checksums_sha256 = data ["checksums_sha256" ]
1049- build .stage0_compiler = Stage0Toolchain (data ["compiler" ])
1050- build .download_url = os .getenv ("RUSTUP_DIST_SERVER" ) or data ["config" ]["dist_server" ]
1051-
1052- build .build = args .build or build .build_triple ()
1053-
10541066 if not os .path .exists (build .build_dir ):
10551067 os .makedirs (build .build_dir )
10561068
10571069 # Fetch/build the bootstrap
10581070 build .download_toolchain ()
10591071 sys .stdout .flush ()
1060- build .build_bootstrap (args . color , args . warnings , verbose_count )
1072+ build .build_bootstrap ()
10611073 sys .stdout .flush ()
10621074
10631075 # Run the bootstrap
@@ -1077,7 +1089,7 @@ def main():
10771089 if len (sys .argv ) > 1 and sys .argv [1 ] == 'help' :
10781090 sys .argv [1 ] = '-h'
10791091
1080- args = parse_args ()
1092+ args = parse_args (sys . argv )
10811093 help_triggered = args .help or len (sys .argv ) == 1
10821094
10831095 # If the user is asking for help, let them know that the whole download-and-build
0 commit comments