2
2
//! point) and linking it into the process binary. Requires out_dir to be added
3
3
//! to rustc's link search path.
4
4
5
- /// Compiles the external assembly and tells cargo/rustc to link the resulting
6
- /// library into the crate. Panics if it is unable to find a working assembly
7
- /// toolchain or if the assembly fails to compile.
8
- pub ( crate ) fn build_and_link ( out_dir : & str ) {
9
- use std:: env:: var;
10
- let arch = var ( "CARGO_CFG_TARGET_ARCH" ) . expect ( "Unable to read CARGO_CFG_TARGET_ARCH" ) ;
11
-
12
- // Identify the toolchain configurations to try for the target architecture.
13
- // We support trying multiple toolchains because not all toolchains are
14
- // available on every OS that we want to support development on.
15
- let build_configs: & [ AsmBuildConfig ] = match arch. as_str ( ) {
16
- "arm" => & [ AsmBuildConfig {
17
- prefix : "arm-none-eabi" ,
18
- as_extra_args : & [ ] ,
19
- strip : false ,
20
- } ] ,
21
- "riscv32" => & [
22
- // First try riscv64-unknown-elf, as it is the toolchain used by
23
- // libtock-c and the toolchain used in the CI environment.
24
- AsmBuildConfig {
25
- prefix : "riscv64-unknown-elf" ,
26
- as_extra_args : & [ "-march=rv32imc" ] ,
27
- strip : true ,
28
- } ,
29
- // Second try riscv32-unknown-elf. This is the best match for Tock's
30
- // risc-v targets, but is not as widely available (and has not been
31
- // tested with libtock-rs yet).
32
- AsmBuildConfig {
33
- prefix : "riscv32-unknown-elf" ,
34
- as_extra_args : & [ ] ,
35
- strip : false , // Untested, may need to change.
36
- } ,
37
- // Last try riscv64-linux-gnu, as it is the only option on Debian 10
38
- AsmBuildConfig {
39
- prefix : "riscv64-linux-gnu" ,
40
- as_extra_args : & [ "-march=rv32imc" ] ,
41
- strip : true ,
42
- } ,
43
- ] ,
44
- unknown_arch => {
45
- panic ! ( "Unsupported architecture {}" , unknown_arch) ;
46
- }
47
- } ;
5
+ use std:: {
6
+ env, io,
7
+ process:: { self , Command } ,
8
+ } ;
9
+
10
+ #[ derive( Clone ) ]
11
+ struct ToolchainInfo {
12
+ arch : String ,
13
+
14
+ /// The library archive command.
15
+ ar_cmd : String ,
16
+
17
+ /// The object file strip command, if used.
18
+ /// If `None`, no strip will take place.
19
+ strip_cmd : Option < String > ,
20
+
21
+ /// The assembler command.
22
+ as_cmd : String ,
48
23
49
- // Loop through toolchain configs until one works.
50
- for & build_config in build_configs {
51
- if try_build ( & arch, build_config, out_dir) . is_ok ( ) {
52
- return ;
24
+ /// Additional flags to pass to the assembler.
25
+ as_flags : Vec < String > ,
26
+ }
27
+
28
+ impl ToolchainInfo {
29
+ fn from_gcc_prefix ( arch : & str , prefix : & str , strip : bool ) -> Self {
30
+ Self {
31
+ arch : arch. to_string ( ) ,
32
+ ar_cmd : format ! ( "{}-ar" , prefix) ,
33
+ strip_cmd : strip. then ( || format ! ( "{}-strip" , prefix) ) ,
34
+ as_cmd : format ! ( "{}-as" , prefix) ,
35
+ as_flags : Vec :: new ( ) ,
53
36
}
54
37
}
55
-
56
- panic ! ( "Unable to find a toolchain for architecture {}" , arch) ;
57
38
}
58
39
59
- #[ derive( Clone , Copy ) ]
60
- struct AsmBuildConfig {
61
- // Prefix, which is prepended to the command names.
62
- prefix : & ' static str ,
40
+ /// Reads toolchain info from local environment variables, merging with a
41
+ /// default toolchain if provided.
42
+ ///
43
+ /// If a complete toolchain could not be constructed, this returns `None`.
44
+ fn env_toolchain ( arch : & str , default : Option < ToolchainInfo > ) -> Option < ToolchainInfo > {
45
+ let env_ar_cmd = env:: var ( "AR" ) . ok ( ) ;
46
+ let env_as_cmd = env:: var ( "AS" ) . ok ( ) ;
47
+ // TODO: this cannot handle whitespace in flags.
48
+ let env_as_flags = env:: var ( "ASFLAGS" )
49
+ . ok ( )
50
+ . map ( |x| x. split_ascii_whitespace ( ) . map ( str:: to_string) . collect ( ) ) ;
51
+ let env_strip_cmd = env:: var ( "STRIP" ) . ok ( ) . filter ( |x| !x. is_empty ( ) ) ;
52
+
53
+ let ar_cmd;
54
+ let as_cmd;
55
+ let as_flags;
56
+ let strip_cmd;
57
+ if let Some ( default) = default {
58
+ // TODO: Should this merging occur or be all-or-nothing with
59
+ // environment-provided toolchain variables?
60
+ ar_cmd = env_ar_cmd. unwrap_or ( default. ar_cmd ) ;
61
+ as_cmd = env_as_cmd. unwrap_or ( default. as_cmd ) ;
62
+ as_flags = env_as_flags. unwrap_or ( default. as_flags ) ;
63
+ strip_cmd = env_strip_cmd. or ( default. strip_cmd ) ;
64
+ } else {
65
+ ar_cmd = env_ar_cmd?;
66
+ as_cmd = env_as_cmd?;
67
+ as_flags = env_as_flags. unwrap_or ( Vec :: new ( ) ) ;
68
+ strip_cmd = env_strip_cmd;
69
+ }
70
+
71
+ Some ( ToolchainInfo {
72
+ arch : arch. to_string ( ) ,
73
+ ar_cmd,
74
+ strip_cmd,
75
+ as_cmd,
76
+ as_flags,
77
+ } )
78
+ }
63
79
64
- // Extra arguments to pass to the assembler.
65
- as_extra_args : & ' static [ & ' static str ] ,
80
+ /// Checks a toolchain, by running `--version` on the provided commands.
81
+ fn test_toolchain ( toolchain : & ToolchainInfo ) -> io:: Result < ( ) > {
82
+ Command :: new ( & toolchain. ar_cmd ) . arg ( "--version" ) . status ( ) ?;
83
+ Command :: new ( & toolchain. as_cmd ) . arg ( "--version" ) . status ( ) ?;
84
+ if let Some ( ref path) = toolchain. strip_cmd {
85
+ Command :: new ( path) . arg ( "--version" ) . status ( ) ?;
86
+ }
87
+ Ok ( ( ) )
88
+ }
66
89
67
- // Do we need to strip the object file before packing it into the library
68
- // archive? This should be set to true on platforms where the assembler adds
69
- // local symbols to the object file.
70
- strip : bool ,
90
+ fn find_default_toolchain ( arch : & str ) -> Option < ToolchainInfo > {
91
+ // The default toolchain is the first GCC-like toolchain that works as
92
+ // expected. We support trying multiple toolchains because not all
93
+ // toolchains are available on every OS that we want to support development.
94
+ match arch {
95
+ "arm" => {
96
+ let toolchain = ToolchainInfo :: from_gcc_prefix ( arch, "arm-none-eabi" , false ) ;
97
+ test_toolchain ( & toolchain) . ok ( ) . map ( |_| toolchain)
98
+ }
99
+ "riscv32" => {
100
+ for toolchain in [
101
+ ToolchainInfo {
102
+ as_flags : vec ! [ "-march=rv32imc" . to_string( ) ] ,
103
+ ..ToolchainInfo :: from_gcc_prefix ( arch, "riscv64-unknown-elf" , true )
104
+ } ,
105
+ // strip: false is untested here, may need to change
106
+ ToolchainInfo :: from_gcc_prefix ( arch, "riscv32-unknown-elf" , false ) ,
107
+ ToolchainInfo {
108
+ as_flags : vec ! [ "-march=rv32imc" . to_string( ) ] ,
109
+ ..ToolchainInfo :: from_gcc_prefix ( arch, "riscv64-linux-gnu" , true )
110
+ } ,
111
+ ] {
112
+ if test_toolchain ( & toolchain) . is_ok ( ) {
113
+ return Some ( toolchain) ;
114
+ }
115
+ }
116
+ None
117
+ }
118
+ unknown_arch => {
119
+ panic ! ( "Unsupported architecture {}" , unknown_arch)
120
+ }
121
+ }
71
122
}
72
123
73
- // Indicates the toolchain in the build config is unavailable.
74
- struct ToolchainUnavailable ;
124
+ /// Compiles the external assembly and tells cargo/rustc to link the resulting
125
+ /// library into the crate. Panics if it is unable to find a working assembly
126
+ /// toolchain or if the assembly fails to compile.
127
+ pub ( crate ) fn build_and_link ( out_dir : & str ) {
128
+ let arch = env:: var ( "CARGO_CFG_TARGET_ARCH" ) . expect ( "Unable to read CARGO_CFG_TARGET_ARCH" ) ;
129
+
130
+ match env_toolchain ( & arch, find_default_toolchain ( & arch) ) {
131
+ Some ( toolchain) => build ( & toolchain, out_dir) ,
132
+ None => panic ! ( "Unable to find a toolchain for architecture {}" , arch) ,
133
+ }
134
+ }
75
135
76
- fn try_build (
77
- arch : & str ,
78
- build_config : AsmBuildConfig ,
79
- out_dir : & str ,
80
- ) -> Result < ( ) , ToolchainUnavailable > {
136
+ fn build ( toolchain : & ToolchainInfo , out_dir : & str ) {
81
137
use std:: path:: PathBuf ;
82
- use std:: process:: Command ;
83
138
84
139
// Invoke the assembler to produce an object file.
85
- let asm_source = & format ! ( "asm/asm_{}.S" , arch) ;
140
+ let as_cmd = & toolchain. as_cmd ;
141
+ let asm_source = & format ! ( "asm/asm_{}.S" , toolchain. arch) ;
86
142
let obj_file_path = [ out_dir, "libtock_rt_asm.o" ] . iter ( ) . collect :: < PathBuf > ( ) ;
87
143
let obj_file = obj_file_path. to_str ( ) . expect ( "Non-Unicode obj_file_path" ) ;
88
- let as_result = Command :: new ( format ! ( "{}-as" , build_config . prefix ) )
89
- . args ( build_config . as_extra_args )
144
+ let status = Command :: new ( as_cmd )
145
+ . args ( & toolchain . as_flags )
90
146
. args ( & [ asm_source, "-o" , obj_file] )
91
- . status ( ) ;
92
-
93
- match as_result {
94
- Err ( error) => {
95
- if error. kind ( ) == std:: io:: ErrorKind :: NotFound {
96
- // This `as` command does not exist. Return an error so
97
- // build_an_link can try another config (if one is available).
98
- return Err ( ToolchainUnavailable ) ;
99
- } else {
100
- panic ! ( "Error invoking assembler: {}" , error) ;
101
- }
102
- }
103
- Ok ( status) => {
104
- assert ! ( status. success( ) , "Assembler returned an error" ) ;
105
- }
106
- }
107
-
108
- // At this point, we know this toolchain is installed. We will fail if later
109
- // commands are uninstalled rather than trying a different build config.
147
+ . status ( )
148
+ . unwrap_or_else ( |_| panic ! ( "Failed to invoke {}" , as_cmd) ) ;
149
+ assert ! ( status. success( ) , "{} returned an error" , as_cmd) ;
110
150
111
151
println ! ( "cargo:rerun-if-changed={}" , asm_source) ;
112
152
113
153
// Run `strip` if necessary.
114
- if build_config. strip {
115
- let strip_cmd = format ! ( "{}-strip" , build_config. prefix) ;
116
- let status = Command :: new ( & strip_cmd)
154
+ if let Some ( strip_cmd) = & toolchain. strip_cmd {
155
+ let status = Command :: new ( strip_cmd)
117
156
. args ( & [ "-K" , "start" , "-K" , "rust_start" , obj_file] )
118
157
. status ( )
119
158
. unwrap_or_else ( |_| panic ! ( "Failed to invoke {}" , strip_cmd) ) ;
@@ -127,15 +166,15 @@ fn try_build(
127
166
. iter ( )
128
167
. collect ( ) ;
129
168
if let Err ( error) = std:: fs:: remove_file ( & archive_path) {
130
- if error. kind ( ) != std :: io:: ErrorKind :: NotFound {
169
+ if error. kind ( ) != io:: ErrorKind :: NotFound {
131
170
panic ! ( "Unable to remove archive file {}" , archive_path. display( ) ) ;
132
171
}
133
172
}
134
173
135
174
// Create the library archive.
136
- let ar_cmd = format ! ( "{}-ar" , build_config . prefix ) ;
175
+ let ar_cmd = & toolchain . ar_cmd ;
137
176
let archive = archive_path. to_str ( ) . expect ( "Non-Unicode archive_path" ) ;
138
- let status = std :: process:: Command :: new ( & ar_cmd)
177
+ let status = process:: Command :: new ( ar_cmd)
139
178
// c == Do not complain if archive needs to be created.
140
179
// r == Insert or replace file in archive.
141
180
. args ( & [ "cr" , archive, obj_file] )
@@ -145,6 +184,4 @@ fn try_build(
145
184
146
185
// Tell rustc to link the binary against the library archive.
147
186
println ! ( "cargo:rustc-link-lib=static={}" , ARCHIVE_NAME ) ;
148
-
149
- Ok ( ( ) )
150
187
}
0 commit comments