@@ -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} ;
@@ -475,12 +475,32 @@ impl Build {
475475 /// This will detect if any submodules are out of date an run the necessary
476476 /// commands to sync them all with upstream.
477477 fn update_submodules ( & self ) {
478+ struct Submodule < ' a > {
479+ path : & ' a Path ,
480+ state : State ,
481+ }
482+
483+ enum State {
484+ // The submodule may have staged/unstaged changes
485+ MaybeDirty ,
486+ // Or could be initialized but never updated
487+ NotInitialized ,
488+ // The submodule, itself, has extra commits but those changes haven't been commited to
489+ // the (outer) git repository
490+ OutOfSync ,
491+ }
492+
478493 if !self . config . submodules {
479494 return
480495 }
481496 if fs:: metadata ( self . src . join ( ".git" ) ) . is_err ( ) {
482497 return
483498 }
499+ let git = || {
500+ let mut cmd = Command :: new ( "git" ) ;
501+ cmd. current_dir ( & self . src ) ;
502+ return cmd
503+ } ;
484504 let git_submodule = || {
485505 let mut cmd = Command :: new ( "git" ) ;
486506 cmd. current_dir ( & self . src ) . arg ( "submodule" ) ;
@@ -492,19 +512,60 @@ impl Build {
492512 // of detecting whether we need to run all the submodule commands
493513 // below.
494514 let out = output ( git_submodule ( ) . arg ( "status" ) ) ;
495- if !out. lines ( ) . any ( |l| l. starts_with ( "+" ) || l. starts_with ( "-" ) ) {
496- return
515+ let mut submodules = vec ! [ ] ;
516+ for line in out. lines ( ) {
517+ // NOTE `git submodule status` output looks like this:
518+ //
519+ // -5066b7dcab7e700844b0e2ba71b8af9dc627a59b src/liblibc
520+ // +b37ef24aa82d2be3a3cc0fe89bf82292f4ca181c src/compiler-rt (remotes/origin/rust-llvm-2016-07-18-1-gb37ef24)
521+ // e058ca661692a8d01f8cf9d35939dfe3105ce968 src/jemalloc (3.6.0-533-ge058ca6)
522+ //
523+ // The first character can be '-', '+' or ' ' and denotes the `State` of the submodule
524+ // Right next to this character is the SHA-1 of the submodule HEAD
525+ // And after that comes the path to the submodule
526+ let path = Path :: new ( line[ 1 ..] . split ( ' ' ) . skip ( 1 ) . next ( ) . unwrap ( ) ) ;
527+ let state = if line. starts_with ( '-' ) {
528+ State :: NotInitialized
529+ } else if line. starts_with ( '*' ) {
530+ State :: OutOfSync
531+ } else if line. starts_with ( ' ' ) {
532+ State :: MaybeDirty
533+ } else {
534+ panic ! ( "unexpected git submodule state: {:?}" , line. chars( ) . next( ) ) ;
535+ } ;
536+
537+ submodules. push ( Submodule { path : path, state : state } )
497538 }
498539
499540 self . run ( git_submodule ( ) . arg ( "sync" ) ) ;
500- self . run ( git_submodule ( ) . arg ( "init" ) ) ;
501- self . run ( git_submodule ( ) . arg ( "update" ) ) ;
502- self . run ( git_submodule ( ) . arg ( "update" ) . arg ( "--recursive" ) ) ;
503- self . run ( git_submodule ( ) . arg ( "status" ) . arg ( "--recursive" ) ) ;
504- self . run ( git_submodule ( ) . arg ( "foreach" ) . arg ( "--recursive" )
505- . arg ( "git" ) . arg ( "clean" ) . arg ( "-fdx" ) ) ;
506- self . run ( git_submodule ( ) . arg ( "foreach" ) . arg ( "--recursive" )
507- . arg ( "git" ) . arg ( "checkout" ) . arg ( "." ) ) ;
541+
542+ for submodule in submodules {
543+ // If using llvm-root then don't touch the llvm submodule.
544+ if submodule. path . components ( ) . any ( |c| c == Component :: Normal ( "llvm" . as_ref ( ) ) ) &&
545+ self . config . target_config . get ( & self . config . build ) . and_then ( |c| c. llvm_config . as_ref ( ) ) . is_some ( )
546+ {
547+ continue
548+ }
549+
550+ match submodule. state {
551+ State :: MaybeDirty => {
552+ // drop staged changes
553+ self . run ( git ( ) . arg ( "-C" ) . arg ( submodule. path ) . args ( & [ "reset" , "--hard" ] ) ) ;
554+ // drops unstaged changes
555+ self . run ( git ( ) . arg ( "-C" ) . arg ( submodule. path ) . args ( & [ "clean" , "-fdx" ] ) ) ;
556+ } ,
557+ State :: NotInitialized => {
558+ self . run ( git_submodule ( ) . arg ( "init" ) . arg ( submodule. path ) ) ;
559+ self . run ( git_submodule ( ) . arg ( "update" ) . arg ( submodule. path ) ) ;
560+ } ,
561+ State :: OutOfSync => {
562+ // drops submodule commits that weren't reported to the (outer) git repository
563+ self . run ( git_submodule ( ) . arg ( "update" ) . arg ( submodule. path ) ) ;
564+ self . run ( git ( ) . arg ( "-C" ) . arg ( submodule. path ) . args ( & [ "reset" , "--hard" ] ) ) ;
565+ self . run ( git ( ) . arg ( "-C" ) . arg ( submodule. path ) . args ( & [ "clean" , "-fdx" ] ) ) ;
566+ } ,
567+ }
568+ }
508569 }
509570
510571 /// Clear out `dir` if `input` is newer.
0 commit comments