@@ -8,46 +8,76 @@ defmodule Rustler.Compiler do
88 config = Config . from ( otp_app , config , opts )
99
1010 if ! config . skip_compilation? do
11- crate_full_path = Path . expand ( config . path , File . cwd! ( ) )
12-
1311 File . mkdir_p! ( priv_dir ( ) )
1412
1513 Mix . shell ( ) . info ( "Compiling crate #{ config . crate } in #{ config . mode } mode (#{ config . path } )" )
14+ do_compile ( config )
15+ Mix.Project . build_structure ( )
16+
17+ # See #326: Ensure that the libraries are copied into the correct subdirectory
18+ # in `_build`.
19+ missing = Enum . reject ( expected_files ( config ) , & File . exists? / 1 )
20+
21+ if missing != [ ] do
22+ Mix . shell ( ) . info ( "Expected files do not exist: #{ inspect ( missing ) } , rebuilding" )
23+
24+ do_compile ( config , true )
25+ Mix.Project . build_structure ( )
26+ end
27+
28+ missing = Enum . reject ( expected_files ( config ) , & File . exists? / 1 )
29+
30+ if missing != [ ] do
31+ raise "Expected files do still not exist: #{ inspect ( missing ) } \n Please verify your configuration"
32+ end
33+ end
1634
17- [ cmd | args ] =
18- make_base_command ( config . cargo )
19- |> make_no_default_features_flag ( config . default_features )
20- |> make_features_flag ( config . features )
21- |> make_target_flag ( config . target )
22- |> make_build_mode_flag ( config . mode )
35+ config
36+ end
2337
24- ensure_platform_requirements! ( crate_full_path , config , :os . type ( ) )
38+ defp do_compile ( config , recompile \\ false ) do
39+ [ cmd | args ] =
40+ rustc ( config . cargo )
41+ |> build_flags ( config )
42+ |> make_build_mode_flag ( config . mode )
43+
44+ args =
45+ if recompile do
46+ # HACK: Cargo does not allow forcing a recompilation, but we need the
47+ # compiler-artifact messages, so force a recompilation via a "config
48+ # change"
49+ args ++ [ "--cfg" , "build_#{ System . system_time ( :millisecond ) } " ]
50+ else
51+ args
52+ end
2553
26- compile_result =
27- System . cmd ( cmd , args ,
28- cd: crate_full_path ,
29- stderr_to_stdout: true ,
30- env: [ { "CARGO_TARGET_DIR" , config . target_dir } | config . env ] ,
31- into: IO . stream ( :stdio , :line )
32- )
54+ compile_result =
55+ System . cmd ( cmd , args , env: config . env , into: % Rustler.BuildResults { } , lines: 1024 )
3356
57+ artifacts =
3458 case compile_result do
35- { _ , 0 } -> :ok
59+ { build_results , 0 } -> build_results . artifacts
3660 { _ , code } -> raise "Rust NIF compile error (rustc exit code #{ code } )"
3761 end
3862
39- handle_artifacts ( crate_full_path , config )
40- # See #326: Ensure that the libraries are copied into the correct subdirectory
41- # in `_build`.
42- Mix.Project . build_structure ( )
43- end
63+ handle_artifacts ( artifacts , config )
64+ end
4465
45- config
66+ defp build_flags ( args , config ) do
67+ args
68+ |> make_package_flag ( config . crate )
69+ |> make_no_default_features_flag ( config . default_features )
70+ |> make_features_flag ( config . features )
71+ |> make_target_flag ( config . target )
4672 end
4773
48- defp make_base_command ( :system ) , do: [ "cargo" , "rustc" ]
49- defp make_base_command ( { :system , channel } ) , do: [ "cargo" , channel , "rustc" ]
50- defp make_base_command ( { :bin , path } ) , do: [ path , "rustc" ]
74+ defp rustc ( cargo ) do
75+ make_base_command ( cargo ) ++ [ "rustc" , "--message-format=json-render-diagnostics" , "--quiet" ]
76+ end
77+
78+ defp make_base_command ( :system ) , do: [ "cargo" ]
79+ defp make_base_command ( { :system , channel } ) , do: [ "cargo" , channel ]
80+ defp make_base_command ( { :bin , path } ) , do: [ path ]
5181
5282 defp make_base_command ( { :rustup , version } ) do
5383 if Rustup . version ( ) == :none do
@@ -58,10 +88,10 @@ defmodule Rustler.Compiler do
5888 throw_error ( { :rust_version_not_installed , version } )
5989 end
6090
61- [ "rustup" , "run" , version , "cargo" , "rustc" ]
91+ [ "rustup" , "run" , version , "cargo" ]
6292 end
6393
64- defp ensure_platform_requirements! ( _ , _ , _ ) , do: :ok
94+ defp make_package_flag ( args , crate ) , do: args ++ [ "-p" , " #{ crate } " ]
6595
6696 defp make_no_default_features_flag ( args , true ) , do: args
6797 defp make_no_default_features_flag ( args , false ) , do: args ++ [ "--no-default-features" ]
@@ -99,71 +129,54 @@ defmodule Rustler.Compiler do
99129 end
100130 end
101131
102- defp handle_artifacts ( path , config ) do
103- toml = toml_data ( path )
132+ defp handle_artifacts ( artifacts , config ) do
104133 target = config . target
105- names = get_name ( toml , :lib ) ++ get_name ( toml , :bin )
106134
107- output_dir =
108- if is_binary ( target ) do
109- Path . join ( [ target , Atom . to_string ( config . mode ) ] )
110- else
111- Atom . to_string ( config . mode )
112- end
135+ if artifacts == [ ] do
136+ throw_error ( :no_artifacts )
137+ end
113138
114- Enum . each ( names , fn { name , type } ->
115- { src_file , dst_file } = make_file_names ( name , type , target )
116- compiled_lib = Path . join ( [ config . target_dir , output_dir , src_file ] )
117- destination_lib = Path . join ( priv_dir ( ) , dst_file )
118-
119- # If the file exists already, we delete it first. This is to ensure that another
120- # process, which might have the library dynamically linked in, does not generate
121- # a segfault. By deleting it first, we ensure that the copy operation below does
122- # not write into the existing file.
123- File . rm ( destination_lib )
124- File . cp! ( compiled_lib , destination_lib )
139+ Enum . each ( artifacts , fn artifact = % Rustler.BuildArtifact { } ->
140+ if artifact . kind == :lib or artifact . kind == :bin do
141+ dst_file = output_file ( artifact . name , artifact . kind , target )
142+ destination_lib = Path . join ( priv_dir ( ) , dst_file )
143+
144+ # If the file exists already, we delete it first. This is to ensure that another
145+ # process, which might have the library dynamically linked in, does not generate
146+ # a segfault. By deleting it first, we ensure that the copy operation below does
147+ # not write into the existing file.
148+ Mix . shell ( ) . info ( "Copying #{ artifact . filename } to #{ destination_lib } " )
149+ File . rm ( destination_lib )
150+ File . cp! ( artifact . filename , destination_lib )
151+ end
125152 end )
126153 end
127154
128- defp get_name ( toml , section ) do
129- case toml [ to_string ( section ) ] do
130- nil -> [ ]
131- values when is_map ( values ) -> [ { values [ "name" ] , section } ]
132- values when is_list ( values ) -> Enum . map ( values , & { & 1 [ "name" ] , section } )
133- end
134- end
155+ defp expected_files ( config ) do
156+ lib = if config . lib , do: [ output_file ( config . crate , :lib , config . target ) ] , else: [ ]
157+ bins = for bin <- config . binaries , do: output_file ( bin , :bin , config . target )
135158
136- defp get_suffix ( target , :lib ) do
137- case get_target_os_type ( target ) do
138- { :win32 , _ } -> "dll"
139- { :unix , :darwin } -> "dylib"
140- { :unix , _ } -> "so"
159+ for filename <- lib ++ bins do
160+ Path . join ( priv_dir ( ) , filename )
141161 end
142162 end
143163
144- defp get_suffix ( target , :bin ) do
145- case get_target_os_type ( target ) do
146- { :win32 , _ } -> "exe"
147- { :unix , _ } -> ""
148- end
164+ defp output_file ( base_name , kind , target ) do
165+ "#{ base_name } #{ suffix ( kind , target ) } "
149166 end
150167
151- defp make_file_names ( base_name , :lib , target ) do
152- suffix = get_suffix ( target , :lib )
153-
168+ defp suffix ( :lib , target ) do
154169 case get_target_os_type ( target ) do
155- { :win32 , _ } -> { " #{ base_name } . #{ suffix } " , "lib #{ base_name } . dll"}
156- { :unix , :darwin } -> { "lib #{ base_name } . #{ suffix } " , "lib #{ base_name } . so"}
157- { :unix , _ } -> { "lib #{ base_name } . #{ suffix } " , "lib #{ base_name } . so"}
170+ { :win32 , _ } -> ". dll"
171+ { :unix , :darwin } -> ". so"
172+ { :unix , _ } -> ". so"
158173 end
159174 end
160175
161- defp make_file_names ( base_name , :bin , target ) do
162- suffix = get_suffix ( target , :bin )
163-
176+ defp suffix ( :bin , target ) do
164177 case get_target_os_type ( target ) do
165- { :win32 , _ } -> { " #{ base_name } . #{ suffix } " , " #{ base_name } . exe"}
166- { :unix , _ } -> { base_name , base_name }
178+ { :win32 , _ } -> ". exe"
179+ { :unix , _ } -> ""
167180 end
168181 end
169182
@@ -172,19 +185,5 @@ defmodule Rustler.Compiler do
172185 raise "Compilation error"
173186 end
174187
175- defp toml_data ( path ) do
176- if ! File . dir? ( path ) do
177- throw_error ( { :nonexistent_crate_directory , path } )
178- end
179-
180- case File . read ( "#{ path } /Cargo.toml" ) do
181- { :error , :enoent } ->
182- throw_error ( { :cargo_toml_not_found , path } )
183-
184- { :ok , text } ->
185- Toml . decode! ( text )
186- end
187- end
188-
189188 defp priv_dir , do: "priv/native"
190189end
0 commit comments