@@ -32,7 +32,7 @@ use std::cell::RefCell;
3232use std:: collections:: HashMap ;
3333use std:: env;
3434use std:: fs:: { self , File } ;
35- use std:: path:: { PathBuf , Path } ;
35+ use std:: path:: { Component , PathBuf , Path } ;
3636use std:: process:: Command ;
3737
3838use build_helper:: { run_silent, output} ;
@@ -477,12 +477,32 @@ impl Build {
477477 /// This will detect if any submodules are out of date an run the necessary
478478 /// commands to sync them all with upstream.
479479 fn update_submodules ( & self ) {
480+ struct Submodule < ' a > {
481+ path : & ' a Path ,
482+ state : State ,
483+ }
484+
485+ enum State {
486+ // The submodule may have staged/unstaged changes
487+ MaybeDirty ,
488+ // Or could be initialized but never updated
489+ NotInitialized ,
490+ // The submodule, itself, has extra commits but those changes haven't been commited to
491+ // the (outer) git repository
492+ OutOfSync ,
493+ }
494+
480495 if !self . config . submodules {
481496 return
482497 }
483498 if fs:: metadata ( self . src . join ( ".git" ) ) . is_err ( ) {
484499 return
485500 }
501+ let git = || {
502+ let mut cmd = Command :: new ( "git" ) ;
503+ cmd. current_dir ( & self . src ) ;
504+ return cmd
505+ } ;
486506 let git_submodule = || {
487507 let mut cmd = Command :: new ( "git" ) ;
488508 cmd. current_dir ( & self . src ) . arg ( "submodule" ) ;
@@ -494,19 +514,67 @@ impl Build {
494514 // of detecting whether we need to run all the submodule commands
495515 // below.
496516 let out = output ( git_submodule ( ) . arg ( "status" ) ) ;
497- if !out. lines ( ) . any ( |l| l. starts_with ( "+" ) || l. starts_with ( "-" ) ) {
498- return
517+ let mut submodules = vec ! [ ] ;
518+ for line in out. lines ( ) {
519+ // NOTE `git submodule status` output looks like this:
520+ //
521+ // -5066b7dcab7e700844b0e2ba71b8af9dc627a59b src/liblibc
522+ // +b37ef24aa82d2be3a3cc0fe89bf82292f4ca181c src/compiler-rt (remotes/origin/..)
523+ // e058ca661692a8d01f8cf9d35939dfe3105ce968 src/jemalloc (3.6.0-533-ge058ca6)
524+ //
525+ // The first character can be '-', '+' or ' ' and denotes the `State` of the submodule
526+ // Right next to this character is the SHA-1 of the submodule HEAD
527+ // And after that comes the path to the submodule
528+ let path = Path :: new ( line[ 1 ..] . split ( ' ' ) . skip ( 1 ) . next ( ) . unwrap ( ) ) ;
529+ let state = if line. starts_with ( '-' ) {
530+ State :: NotInitialized
531+ } else if line. starts_with ( '*' ) {
532+ State :: OutOfSync
533+ } else if line. starts_with ( ' ' ) {
534+ State :: MaybeDirty
535+ } else {
536+ panic ! ( "unexpected git submodule state: {:?}" , line. chars( ) . next( ) ) ;
537+ } ;
538+
539+ submodules. push ( Submodule { path : path, state : state } )
499540 }
500541
501542 self . run ( git_submodule ( ) . arg ( "sync" ) ) ;
502- self . run ( git_submodule ( ) . arg ( "init" ) ) ;
503- self . run ( git_submodule ( ) . arg ( "update" ) ) ;
504- self . run ( git_submodule ( ) . arg ( "update" ) . arg ( "--recursive" ) ) ;
505- self . run ( git_submodule ( ) . arg ( "status" ) . arg ( "--recursive" ) ) ;
506- self . run ( git_submodule ( ) . arg ( "foreach" ) . arg ( "--recursive" )
507- . arg ( "git" ) . arg ( "clean" ) . arg ( "-fdx" ) ) ;
508- self . run ( git_submodule ( ) . arg ( "foreach" ) . arg ( "--recursive" )
509- . arg ( "git" ) . arg ( "checkout" ) . arg ( "." ) ) ;
543+
544+ for submodule in submodules {
545+ // If using llvm-root then don't touch the llvm submodule.
546+ if submodule. path . components ( ) . any ( |c| c == Component :: Normal ( "llvm" . as_ref ( ) ) ) &&
547+ self . config . target_config . get ( & self . config . build )
548+ . and_then ( |c| c. llvm_config . as_ref ( ) ) . is_some ( )
549+ {
550+ continue
551+ }
552+
553+ if submodule. path . components ( ) . any ( |c| c == Component :: Normal ( "jemalloc" . as_ref ( ) ) ) &&
554+ !self . config . use_jemalloc
555+ {
556+ continue
557+ }
558+
559+ match submodule. state {
560+ State :: MaybeDirty => {
561+ // drop staged changes
562+ self . run ( git ( ) . arg ( "-C" ) . arg ( submodule. path ) . args ( & [ "reset" , "--hard" ] ) ) ;
563+ // drops unstaged changes
564+ self . run ( git ( ) . arg ( "-C" ) . arg ( submodule. path ) . args ( & [ "clean" , "-fdx" ] ) ) ;
565+ } ,
566+ State :: NotInitialized => {
567+ self . run ( git_submodule ( ) . arg ( "init" ) . arg ( submodule. path ) ) ;
568+ self . run ( git_submodule ( ) . arg ( "update" ) . arg ( submodule. path ) ) ;
569+ } ,
570+ State :: OutOfSync => {
571+ // drops submodule commits that weren't reported to the (outer) git repository
572+ self . run ( git_submodule ( ) . arg ( "update" ) . arg ( submodule. path ) ) ;
573+ self . run ( git ( ) . arg ( "-C" ) . arg ( submodule. path ) . args ( & [ "reset" , "--hard" ] ) ) ;
574+ self . run ( git ( ) . arg ( "-C" ) . arg ( submodule. path ) . args ( & [ "clean" , "-fdx" ] ) ) ;
575+ } ,
576+ }
577+ }
510578 }
511579
512580 /// Clear out `dir` if `input` is newer.
0 commit comments