@@ -3,7 +3,7 @@ use crate::util::errors::CargoResult;
33use crate :: util:: important_paths:: find_root_manifest_for_wd;
44use crate :: util:: { FossilRepo , GitRepo , HgRepo , PijulRepo , existing_vcs_repo} ;
55use crate :: util:: { GlobalContext , restricted_names} ;
6- use anyhow:: { Context as _, anyhow} ;
6+ use anyhow:: { Context as _, anyhow, bail } ;
77use cargo_util:: paths:: { self , write_atomic} ;
88use cargo_util_schemas:: manifest:: PackageName ;
99use serde:: Deserialize ;
@@ -65,28 +65,32 @@ pub struct NewOptions {
6565#[ derive( Clone , Copy , Debug , PartialEq , Eq ) ]
6666pub enum NewProjectKind {
6767 Bin ,
68- Lib ,
68+ Lib { proc_macro : bool } ,
6969}
7070
7171impl NewProjectKind {
7272 fn is_bin ( self ) -> bool {
7373 self == NewProjectKind :: Bin
7474 }
75+ fn is_proc_macro ( self ) -> bool {
76+ self == NewProjectKind :: Lib { proc_macro : true }
77+ }
7578}
7679
7780impl fmt:: Display for NewProjectKind {
7881 fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
7982 match * self {
8083 NewProjectKind :: Bin => "binary (application)" ,
81- NewProjectKind :: Lib => "library" ,
84+ NewProjectKind :: Lib { proc_macro : false } => "library" ,
85+ NewProjectKind :: Lib { proc_macro : true } => "library (proc-macro)" ,
8286 }
8387 . fmt ( f)
8488 }
8589}
8690
8791struct SourceFileInformation {
8892 relative_path : String ,
89- bin : bool ,
93+ kind : NewProjectKind ,
9094}
9195
9296struct MkOptions < ' a > {
@@ -103,17 +107,24 @@ impl NewOptions {
103107 version_control : Option < VersionControl > ,
104108 bin : bool ,
105109 lib : bool ,
110+ proc_macro : bool ,
106111 path : PathBuf ,
107112 name : Option < String > ,
108113 edition : Option < String > ,
109114 registry : Option < String > ,
110115 ) -> CargoResult < NewOptions > {
111- let auto_detect_kind = !bin && !lib;
112-
113- let kind = match ( bin, lib) {
114- ( true , true ) => anyhow:: bail!( "can't specify both lib and binary outputs" ) ,
115- ( false , true ) => NewProjectKind :: Lib ,
116- ( _, false ) => NewProjectKind :: Bin ,
116+ let auto_detect_kind = !bin && !lib && !proc_macro;
117+
118+ let kind = match ( bin, lib, proc_macro) {
119+ ( true , false , false ) | ( false , false , false ) => NewProjectKind :: Bin ,
120+ ( false , true , false ) => NewProjectKind :: Lib { proc_macro : false } ,
121+ ( false , false , true ) => NewProjectKind :: Lib { proc_macro : true } ,
122+
123+ // invalid
124+ ( true , true , false ) => bail ! ( "can't specify both binary and library outputs" ) ,
125+ ( true , false , true ) => bail ! ( "can't specify both binary and proc-macro outputs" ) ,
126+ ( false , true , true ) => bail ! ( "can't specify both library and proc-macro outputs" ) ,
127+ ( true , true , true ) => bail ! ( "can't specify all binary, library and proc-macro outputs" ) ,
117128 } ;
118129
119130 let opts = NewOptions {
@@ -343,18 +354,27 @@ fn detect_source_paths_and_types(
343354 let sfi = match i. handling {
344355 H :: Bin => SourceFileInformation {
345356 relative_path : pp,
346- bin : true ,
357+ kind : NewProjectKind :: Bin ,
347358 } ,
348359 H :: Lib => SourceFileInformation {
349- relative_path : pp,
350- bin : false ,
360+ relative_path : pp. clone ( ) ,
361+ kind : NewProjectKind :: Lib {
362+ proc_macro : paths:: read ( & path. join ( pp. clone ( ) ) ) ?
363+ . contains ( "proc_macro::TokenStream" ) ,
364+ } ,
351365 } ,
352366 H :: Detect => {
353367 let content = paths:: read ( & path. join ( pp. clone ( ) ) ) ?;
354- let isbin = content. contains ( "fn main" ) ;
368+ let kind = if content. contains ( "fn main" ) {
369+ NewProjectKind :: Bin
370+ } else if content. contains ( "proc_macro::TokenStream" ) {
371+ NewProjectKind :: Lib { proc_macro : true }
372+ } else {
373+ NewProjectKind :: Lib { proc_macro : false }
374+ } ;
355375 SourceFileInformation {
356376 relative_path : pp,
357- bin : isbin ,
377+ kind ,
358378 }
359379 }
360380 } ;
@@ -367,7 +387,7 @@ fn detect_source_paths_and_types(
367387 let mut duplicates_checker: BTreeMap < & str , & SourceFileInformation > = BTreeMap :: new ( ) ;
368388
369389 for i in detected_files {
370- if i. bin {
390+ if i. kind . is_bin ( ) {
371391 if let Some ( x) = BTreeMap :: get :: < str > ( & duplicates_checker, & name) {
372392 anyhow:: bail!(
373393 "\
@@ -397,17 +417,15 @@ cannot automatically generate Cargo.toml as the main target would be ambiguous",
397417 Ok ( ( ) )
398418}
399419
400- fn plan_new_source_file ( bin : bool ) -> SourceFileInformation {
401- if bin {
402- SourceFileInformation {
403- relative_path : "src/main.rs" . to_string ( ) ,
404- bin : true ,
405- }
420+ fn plan_new_source_file ( kind : NewProjectKind ) -> SourceFileInformation {
421+ let relative_path = if kind. is_bin ( ) {
422+ "src/main.rs" . to_string ( )
406423 } else {
407- SourceFileInformation {
408- relative_path : "src/lib.rs" . to_string ( ) ,
409- bin : false ,
410- }
424+ "src/lib.rs" . to_string ( )
425+ } ;
426+ SourceFileInformation {
427+ relative_path,
428+ kind,
411429 }
412430}
413431
@@ -416,10 +434,10 @@ fn calculate_new_project_kind(
416434 auto_detect_kind : bool ,
417435 found_files : & Vec < SourceFileInformation > ,
418436) -> NewProjectKind {
419- let bin_file = found_files. iter ( ) . find ( |x| x. bin ) ;
437+ let bin_file = found_files. iter ( ) . find ( |x| x. kind . is_bin ( ) ) ;
420438
421439 let kind_from_files = if !found_files. is_empty ( ) && bin_file. is_none ( ) {
422- NewProjectKind :: Lib
440+ NewProjectKind :: Lib { proc_macro : false }
423441 } else {
424442 NewProjectKind :: Bin
425443 } ;
@@ -454,7 +472,7 @@ pub fn new(opts: &NewOptions, gctx: &GlobalContext) -> CargoResult<()> {
454472 version_control : opts. version_control ,
455473 path,
456474 name,
457- source_files : vec ! [ plan_new_source_file( opts. kind. is_bin ( ) ) ] ,
475+ source_files : vec ! [ plan_new_source_file( opts. kind) ] ,
458476 edition : opts. edition . as_deref ( ) ,
459477 registry : opts. registry . as_deref ( ) ,
460478 } ;
@@ -488,34 +506,31 @@ pub fn init(opts: &NewOptions, gctx: &GlobalContext) -> CargoResult<NewProjectKi
488506 }
489507 check_path ( path, & mut gctx. shell ( ) ) ?;
490508
491- let has_bin = kind. is_bin ( ) ;
492-
493509 if src_paths_types. is_empty ( ) {
494- src_paths_types. push ( plan_new_source_file ( has_bin) ) ;
495- } else if src_paths_types. len ( ) == 1 && !src_paths_types. iter ( ) . any ( |x| x. bin == has_bin) {
510+ src_paths_types. push ( plan_new_source_file ( kind) ) ;
511+ } else if let Ok ( file) = itertools:: Itertools :: exactly_one ( src_paths_types. iter_mut ( ) )
512+ && file. kind != kind
513+ {
496514 // we've found the only file and it's not the type user wants. Change the type and warn
497- let file_type = if src_paths_types[ 0 ] . bin {
498- NewProjectKind :: Bin
499- } else {
500- NewProjectKind :: Lib
501- } ;
502515 gctx. shell ( ) . warn ( format ! (
503516 "file `{}` seems to be a {} file" ,
504- src_paths_types [ 0 ] . relative_path, file_type
517+ file . relative_path, file . kind
505518 ) ) ?;
506- src_paths_types[ 0 ] . bin = has_bin
507- } else if src_paths_types. len ( ) > 1 && !has_bin {
519+ file. kind = kind;
520+ } else if let [ first, second, ..] = & src_paths_types[ ..]
521+ && !kind. is_bin ( )
522+ {
508523 // We have found both lib and bin files and the user would like us to treat both as libs
509524 anyhow:: bail!(
510525 "cannot have a package with \
511526 multiple libraries, \
512527 found both `{}` and `{}`",
513- src_paths_types [ 0 ] . relative_path,
514- src_paths_types [ 1 ] . relative_path
528+ first . relative_path,
529+ second . relative_path
515530 )
516531 }
517532
518- check_name ( name, opts. name . is_none ( ) , has_bin , & mut gctx. shell ( ) ) ?;
533+ check_name ( name, opts. name . is_none ( ) , kind . is_bin ( ) , & mut gctx. shell ( ) ) ?;
519534
520535 let mut version_control = opts. version_control ;
521536
@@ -781,7 +796,7 @@ fn mk(gctx: &GlobalContext, opts: &MkOptions<'_>) -> CargoResult<()> {
781796
782797 // Calculate what `[lib]` and `[[bin]]`s we need to append to `Cargo.toml`.
783798 for i in & opts. source_files {
784- if i. bin {
799+ if i. kind . is_bin ( ) {
785800 if i. relative_path != "src/main.rs" {
786801 let mut bin = toml_edit:: Table :: new ( ) ;
787802 bin[ "name" ] = toml_edit:: value ( name) ;
@@ -794,10 +809,18 @@ fn mk(gctx: &GlobalContext, opts: &MkOptions<'_>) -> CargoResult<()> {
794809 . expect ( "bin is an array of tables" )
795810 . push ( bin) ;
796811 }
797- } else if i. relative_path != "src/lib.rs" {
798- let mut lib = toml_edit:: Table :: new ( ) ;
799- lib[ "path" ] = toml_edit:: value ( i. relative_path . clone ( ) ) ;
800- manifest[ "lib" ] = toml_edit:: Item :: Table ( lib) ;
812+ } else {
813+ if i. relative_path != "src/lib.rs" {
814+ let mut lib = toml_edit:: Table :: new ( ) ;
815+ lib[ "path" ] = toml_edit:: value ( i. relative_path . clone ( ) ) ;
816+ manifest[ "lib" ] = toml_edit:: Item :: Table ( lib) ;
817+ }
818+ if i. kind . is_proc_macro ( ) {
819+ if manifest. get ( "lib" ) . is_none ( ) {
820+ manifest[ "lib" ] = toml_edit:: Item :: Table ( toml_edit:: Table :: new ( ) ) ;
821+ }
822+ manifest[ "lib" ] [ "proc-macro" ] = true . into ( ) ;
823+ }
801824 }
802825 }
803826
@@ -867,14 +890,16 @@ fn mk(gctx: &GlobalContext, opts: &MkOptions<'_>) -> CargoResult<()> {
867890 paths:: create_dir_all ( src_dir) ?;
868891 }
869892
870- let default_file_content: & [ u8 ] = if i. bin {
871- b"\
893+ let default_file_content: & [ u8 ] = match i. kind {
894+ NewProjectKind :: Bin => {
895+ b"\
872896 fn main() {
873897 println!(\" Hello, world!\" );
874898}
875899"
876- } else {
877- b"\
900+ }
901+ NewProjectKind :: Lib { proc_macro : false } => {
902+ b"\
878903 pub fn add(left: u64, right: u64) -> u64 {
879904 left + right
880905}
@@ -890,6 +915,29 @@ mod tests {
890915 }
891916}
892917"
918+ }
919+ NewProjectKind :: Lib { proc_macro : true } => {
920+ b"\
921+ #[proc_macro]
922+ pub fn identity_proc_macro(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
923+ input
924+ }
925+
926+ #[proc_macro_derive(Foo, attributes(foo))]
927+ pub fn identity_derive_macro(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
928+ input
929+ }
930+
931+ #[proc_macro_attribute]
932+ pub fn identity_attribute_macro(
933+ args: proc_macro::TokenStream,
934+ input: proc_macro::TokenStream,
935+ ) -> proc_macro::TokenStream {
936+ let _ = args;
937+ input
938+ }
939+ "
940+ }
893941 } ;
894942
895943 if !path_of_source_file. is_file ( ) {
0 commit comments