@@ -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" ]
@@ -102,71 +132,54 @@ defmodule Rustler.Compiler do
102132 end
103133 end
104134
105- defp handle_artifacts ( path , config ) do
106- toml = toml_data ( path )
135+ defp handle_artifacts ( artifacts , config ) do
107136 target = config . target
108- names = get_name ( toml , :lib ) ++ get_name ( toml , :bin )
109137
110- output_dir =
111- if is_binary ( target ) do
112- Path . join ( [ target , Atom . to_string ( config . mode ) ] )
113- else
114- Atom . to_string ( config . mode )
115- end
138+ if artifacts == [ ] do
139+ throw_error ( :no_artifacts )
140+ end
116141
117- Enum . each ( names , fn { name , type } ->
118- { src_file , dst_file } = make_file_names ( name , type , target )
119- compiled_lib = Path . join ( [ config . target_dir , output_dir , src_file ] )
120- destination_lib = Path . join ( priv_dir ( ) , dst_file )
121-
122- # If the file exists already, we delete it first. This is to ensure that another
123- # process, which might have the library dynamically linked in, does not generate
124- # a segfault. By deleting it first, we ensure that the copy operation below does
125- # not write into the existing file.
126- File . rm ( destination_lib )
127- File . cp! ( compiled_lib , destination_lib )
142+ Enum . each ( artifacts , fn artifact = % Rustler.BuildArtifact { } ->
143+ if artifact . kind == :lib or artifact . kind == :bin do
144+ dst_file = output_file ( artifact . name , artifact . kind , target )
145+ destination_lib = Path . join ( priv_dir ( ) , dst_file )
146+
147+ # If the file exists already, we delete it first. This is to ensure that another
148+ # process, which might have the library dynamically linked in, does not generate
149+ # a segfault. By deleting it first, we ensure that the copy operation below does
150+ # not write into the existing file.
151+ Mix . shell ( ) . info ( "Copying #{ artifact . filename } to #{ destination_lib } " )
152+ File . rm ( destination_lib )
153+ File . cp! ( artifact . filename , destination_lib )
154+ end
128155 end )
129156 end
130157
131- defp get_name ( toml , section ) do
132- case toml [ to_string ( section ) ] do
133- nil -> [ ]
134- values when is_map ( values ) -> [ { values [ "name" ] , section } ]
135- values when is_list ( values ) -> Enum . map ( values , & { & 1 [ "name" ] , section } )
136- end
137- end
158+ defp expected_files ( config ) do
159+ lib = if config . lib , do: [ output_file ( config . crate , :lib , config . target ) ] , else: [ ]
160+ bins = for bin <- config . binaries , do: output_file ( bin , :bin , config . target )
138161
139- defp get_suffix ( target , :lib ) do
140- case get_target_os_type ( target ) do
141- { :win32 , _ } -> "dll"
142- { :unix , :darwin } -> "dylib"
143- { :unix , _ } -> "so"
162+ for filename <- lib ++ bins do
163+ Path . join ( priv_dir ( ) , filename )
144164 end
145165 end
146166
147- defp get_suffix ( target , :bin ) do
148- case get_target_os_type ( target ) do
149- { :win32 , _ } -> "exe"
150- { :unix , _ } -> ""
151- end
167+ defp output_file ( base_name , kind , target ) do
168+ "#{ base_name } #{ suffix ( kind , target ) } "
152169 end
153170
154- defp make_file_names ( base_name , :lib , target ) do
155- suffix = get_suffix ( target , :lib )
156-
171+ defp suffix ( :lib , target ) do
157172 case get_target_os_type ( target ) do
158- { :win32 , _ } -> { " #{ base_name } . #{ suffix } " , "lib #{ base_name } . dll"}
159- { :unix , :darwin } -> { "lib #{ base_name } . #{ suffix } " , "lib #{ base_name } . so"}
160- { :unix , _ } -> { "lib #{ base_name } . #{ suffix } " , "lib #{ base_name } . so"}
173+ { :win32 , _ } -> ". dll"
174+ { :unix , :darwin } -> ". so"
175+ { :unix , _ } -> ". so"
161176 end
162177 end
163178
164- defp make_file_names ( base_name , :bin , target ) do
165- suffix = get_suffix ( target , :bin )
166-
179+ defp suffix ( :bin , target ) do
167180 case get_target_os_type ( target ) do
168- { :win32 , _ } -> { " #{ base_name } . #{ suffix } " , " #{ base_name } . exe"}
169- { :unix , _ } -> { base_name , base_name }
181+ { :win32 , _ } -> ". exe"
182+ { :unix , _ } -> ""
170183 end
171184 end
172185
@@ -175,19 +188,5 @@ defmodule Rustler.Compiler do
175188 raise "Compilation error"
176189 end
177190
178- defp toml_data ( path ) do
179- if ! File . dir? ( path ) do
180- throw_error ( { :nonexistent_crate_directory , path } )
181- end
182-
183- case File . read ( "#{ path } /Cargo.toml" ) do
184- { :error , :enoent } ->
185- throw_error ( { :cargo_toml_not_found , path } )
186-
187- { :ok , text } ->
188- Toml . decode! ( text )
189- end
190- end
191-
192191 defp priv_dir , do: "priv/native"
193192end
0 commit comments