1- use std:: fs:: read_to_string ;
1+ use std:: fs;
22use std:: path:: { Path , PathBuf } ;
33use std:: process:: Stdio ;
44
5- use anyhow:: { anyhow , bail, Context } ;
6- use cargo_metadata:: { Metadata , Package } ;
5+ use anyhow:: { bail, Context , Result } ;
6+ use cargo_metadata:: { Metadata , Package , Target } ;
77use shuttle_common:: constants:: RUNTIME_NAME ;
88use tokio:: io:: AsyncBufReadExt ;
9- use tracing:: { debug , error, info , trace} ;
9+ use tracing:: { error, trace} ;
1010
11- #[ derive( Clone , Debug , Eq , PartialEq ) ]
1211/// This represents a compiled Shuttle service
12+ #[ derive( Clone , Debug , Eq , PartialEq ) ]
1313pub struct BuiltService {
1414 pub workspace_path : PathBuf ,
15- pub manifest_path : PathBuf ,
16- pub package_name : String ,
15+ pub target_name : String ,
1716 pub executable_path : PathBuf ,
1817}
1918
20- impl BuiltService {
21- /// The directory that contains the crate (that Cargo.toml is in)
22- pub fn crate_directory ( & self ) -> & Path {
23- self . manifest_path
24- . parent ( )
25- . expect ( "manifest to be in a directory" )
26- }
27-
28- /// Try to get the service name of a crate from Shuttle.toml in the crate root, if it doesn't
29- /// exist get it from the Cargo.toml package name of the crate.
30- pub fn service_name ( & self ) -> anyhow:: Result < String > {
31- let shuttle_toml_path = self . crate_directory ( ) . join ( "Shuttle.toml" ) ;
32-
33- match extract_shuttle_toml_name ( shuttle_toml_path) {
34- Ok ( service_name) => Ok ( service_name) ,
35- Err ( error) => {
36- debug ! ( ?error, "failed to get service name from Shuttle.toml" ) ;
37-
38- // Couldn't get name from Shuttle.toml, use package name instead.
39- Ok ( self . package_name . clone ( ) )
40- }
41- }
42- }
43- }
44-
45- fn extract_shuttle_toml_name ( path : PathBuf ) -> anyhow:: Result < String > {
46- let shuttle_toml =
47- read_to_string ( path. as_path ( ) ) . map_err ( |_| anyhow ! ( "{} not found" , path. display( ) ) ) ?;
48-
49- let toml: toml:: Value =
50- toml:: from_str ( & shuttle_toml) . context ( "failed to parse Shuttle.toml" ) ?;
51-
52- let name = toml
53- . get ( "name" )
54- . context ( "couldn't find `name` key in Shuttle.toml" ) ?
55- . as_str ( )
56- . context ( "`name` key in Shuttle.toml must be a string" ) ?
57- . to_string ( ) ;
58-
59- Ok ( name)
60- }
61-
62- /// Given a project directory path, builds the crate
19+ /// Builds Shuttle service in given project directory
6320pub async fn build_workspace (
6421 project_path : & Path ,
6522 release_mode : bool ,
6623 tx : tokio:: sync:: mpsc:: Sender < String > ,
67- deployment : bool ,
68- ) -> anyhow:: Result < Vec < BuiltService > > {
24+ ) -> Result < BuiltService > {
6925 let project_path = project_path. to_owned ( ) ;
7026 let manifest_path = project_path. join ( "Cargo.toml" ) ;
7127 if !manifest_path. exists ( ) {
@@ -100,29 +56,23 @@ pub async fn build_workspace(
10056 notification. abort ( ) ;
10157
10258 let metadata = async_cargo_metadata ( manifest_path. as_path ( ) ) . await ?;
103- let packages = find_shuttle_packages ( & metadata) ?;
104- if packages. is_empty ( ) {
105- bail ! (
106- "Did not find any packages that Shuttle can run. \
107- Make sure your crate has a binary target that uses `#[shuttle_runtime::main]`."
108- ) ;
109- }
59+ let ( package, target) = find_first_shuttle_package ( & metadata) ?;
11060
111- let services = compile (
112- packages,
61+ let service = cargo_build (
62+ package,
63+ target,
11364 release_mode,
11465 project_path. clone ( ) ,
11566 metadata. target_directory . clone ( ) ,
116- deployment,
11767 tx. clone ( ) ,
11868 )
11969 . await ?;
120- trace ! ( "packages compiled" ) ;
70+ trace ! ( "package compiled" ) ;
12171
122- Ok ( services )
72+ Ok ( service )
12373}
12474
125- pub async fn async_cargo_metadata ( manifest_path : & Path ) -> anyhow :: Result < Metadata > {
75+ pub async fn async_cargo_metadata ( manifest_path : & Path ) -> Result < Metadata > {
12676 let metadata = {
12777 // Modified implementaion of `cargo_metadata::MetadataCommand::exec` (from v0.15.3).
12878 // Uses tokio Command instead of std, to make this operation non-blocking.
@@ -149,63 +99,62 @@ pub async fn async_cargo_metadata(manifest_path: &Path) -> anyhow::Result<Metada
14999 Ok ( metadata)
150100}
151101
152- pub fn find_shuttle_packages ( metadata : & Metadata ) -> anyhow:: Result < Vec < Package > > {
102+ /// Find crates with a runtime dependency and main macro
103+ fn find_shuttle_packages ( metadata : & Metadata ) -> Result < Vec < ( Package , Target ) > > {
153104 let mut packages = Vec :: new ( ) ;
105+ trace ! ( "Finding Shuttle-related packages" ) ;
154106 for member in metadata. workspace_packages ( ) {
155- // skip non-Shuttle-related crates
156- // (must have runtime dependency and not be just a library)
157107 let has_runtime_dep = member
158108 . dependencies
159109 . iter ( )
160110 . any ( |dependency| dependency. name == RUNTIME_NAME ) ;
161- let has_binary_target = member . targets . iter ( ) . any ( |t| t . is_bin ( ) ) ;
162- if ! ( has_runtime_dep && has_binary_target ) {
111+ if !has_runtime_dep {
112+ trace ! ( "Skipping {}, no shuttle-runtime dependency" , member . name ) ;
163113 continue ;
164114 }
165115
166- let mut shuttle_deps = member
167- . dependencies
168- . iter ( )
169- . filter ( |& d| d. name . starts_with ( "shuttle-" ) )
170- . map ( |d| format ! ( "{} '{}'" , d. name, d. req) )
171- . collect :: < Vec < _ > > ( ) ;
172- shuttle_deps. sort ( ) ;
173- info ! ( name = member. name, deps = ?shuttle_deps, "Found workspace member with shuttle dependencies" ) ;
174- packages. push ( member. to_owned ( ) ) ;
116+ let mut target = None ;
117+ for t in member. targets . iter ( ) {
118+ if t. is_bin ( )
119+ && fs:: read_to_string ( t. src_path . as_std_path ( ) )
120+ . context ( "reading to check for shuttle macro" ) ?
121+ // don't look for the last ] so that we also find instances of `#[shuttle_runtime::main(...)]`
122+ . contains ( "#[shuttle_runtime::main" )
123+ {
124+ target = Some ( t) ;
125+ break ;
126+ }
127+ }
128+ let Some ( target) = target else {
129+ trace ! (
130+ "Skipping {}, no binary target with a #[shuttle_runtime::main] macro" ,
131+ member. name
132+ ) ;
133+ continue ;
134+ } ;
135+
136+ trace ! ( "Found {}" , member. name) ;
137+ packages. push ( ( member. to_owned ( ) , target. to_owned ( ) ) ) ;
175138 }
176139
177140 Ok ( packages)
178141}
179142
180- // Only used in deployer
181- pub async fn clean_crate ( project_path : & Path ) -> anyhow:: Result < ( ) > {
182- let manifest_path = project_path. join ( "Cargo.toml" ) ;
183- if !manifest_path. exists ( ) {
184- bail ! ( "Cargo manifest file not found: {}" , manifest_path. display( ) ) ;
185- }
186- if !tokio:: process:: Command :: new ( "cargo" )
187- . arg ( "clean" )
188- . arg ( "--manifest-path" )
189- . arg ( & manifest_path)
190- . arg ( "--offline" )
191- . status ( )
192- . await ?
193- . success ( )
194- {
195- bail ! ( "`cargo clean` failed. Did you build anything yet?" ) ;
196- }
197-
198- Ok ( ( ) )
143+ pub fn find_first_shuttle_package ( metadata : & Metadata ) -> Result < ( Package , Target ) > {
144+ find_shuttle_packages ( metadata) ?. into_iter ( ) . next ( ) . context (
145+ "Expected at least one target that Shuttle can build. \
146+ Make sure your crate has a binary target that uses a fully qualified `#[shuttle_runtime::main]`.",
147+ )
199148}
200149
201- async fn compile (
202- packages : Vec < Package > ,
150+ async fn cargo_build (
151+ package : Package ,
152+ target : Target ,
203153 release_mode : bool ,
204154 project_path : PathBuf ,
205155 target_path : impl Into < PathBuf > ,
206- deployment : bool ,
207156 tx : tokio:: sync:: mpsc:: Sender < String > ,
208- ) -> anyhow :: Result < Vec < BuiltService > > {
157+ ) -> Result < BuiltService > {
209158 let manifest_path = project_path. join ( "Cargo.toml" ) ;
210159 if !manifest_path. exists ( ) {
211160 bail ! ( "Cargo manifest file not found: {}" , manifest_path. display( ) ) ;
@@ -221,17 +170,11 @@ async fn compile(
221170 . arg ( "--color=always" ) // piping disables auto color, but we want it
222171 . current_dir ( project_path. as_path ( ) ) ;
223172
224- if deployment {
225- cmd. arg ( "--jobs=4" ) ;
226- }
227-
228- // TODO: Compile only one binary target in the package.
229- for package in & packages {
230- if package. features . contains_key ( "shuttle" ) {
231- cmd. arg ( "--no-default-features" ) . arg ( "--features=shuttle" ) ;
232- }
233- cmd. arg ( "--package" ) . arg ( package. name . as_str ( ) ) ;
173+ if package. features . contains_key ( "shuttle" ) {
174+ cmd. arg ( "--no-default-features" ) . arg ( "--features=shuttle" ) ;
234175 }
176+ cmd. arg ( "--package" ) . arg ( package. name . as_str ( ) ) ;
177+ cmd. arg ( "--bin" ) . arg ( target. name . as_str ( ) ) ;
235178
236179 let profile = if release_mode {
237180 cmd. arg ( "--release" ) ;
@@ -255,30 +198,22 @@ async fn compile(
255198 } ) ;
256199 let status = handle. wait ( ) . await ?;
257200 if !status. success ( ) {
258- bail ! ( "Build failed. Is the Shuttle runtime missing? " ) ;
201+ bail ! ( "Build failed." ) ;
259202 }
260203
261- let services = packages
262- . iter ( )
263- . map ( |package| {
264- let mut path: PathBuf = [
265- project_path. clone ( ) ,
266- target_path. clone ( ) ,
267- profile. into ( ) ,
268- package. name . clone ( ) . into ( ) ,
269- ]
270- . iter ( )
271- . collect ( ) ;
272- path. set_extension ( std:: env:: consts:: EXE_EXTENSION ) ;
273-
274- BuiltService {
275- workspace_path : project_path. clone ( ) ,
276- manifest_path : package. manifest_path . clone ( ) . into_std_path_buf ( ) ,
277- package_name : package. name . clone ( ) ,
278- executable_path : path,
279- }
280- } )
281- . collect ( ) ;
282-
283- Ok ( services)
204+ let mut path: PathBuf = [
205+ project_path. clone ( ) ,
206+ target_path. clone ( ) ,
207+ profile. into ( ) ,
208+ target. name . as_str ( ) . into ( ) ,
209+ ]
210+ . iter ( )
211+ . collect ( ) ;
212+ path. set_extension ( std:: env:: consts:: EXE_EXTENSION ) ;
213+
214+ Ok ( BuiltService {
215+ workspace_path : project_path. clone ( ) ,
216+ target_name : target. name ,
217+ executable_path : path,
218+ } )
284219}
0 commit comments