55# Check committed source mappings without modifying files:
66# ruby tool/mkdepend.rb --scope=all --sources --check
77# The command can be run from either the source or a build directory.
8+ # Known Make variables can be supplied as +VAR=value+. The
9+ # <tt>--thread-model=MODEL</tt> option is equivalent to
10+ # +THREAD_MODEL=MODEL+.
811#
912# Dependency files can declare inputs that do not exist in the source tree:
1013#
@@ -257,17 +260,23 @@ def find_root(path)
257260 # Parses command-line arguments into runner options and input paths.
258261 def parse_options ( argv )
259262 options = { }
263+ make_variables = { }
260264 output = nil
261265 select_mode = proc do |mode |
262266 options [ :mode ] = mode
263267 end
264268 parser = OptionParser . new do |opts |
265- opts . banner = "Usage: #{ File . basename ( $0) } [options] [files]"
269+ opts . banner =
270+ "Usage: #{ File . basename ( $0) } [options] [VAR=value ...] [files]"
266271 opts . separator ""
267272 opts . separator "Input selection:"
268273 opts . on ( "--root=DIR" , "source tree root" ) { |value | options [ :root ] = value }
269- opts . on ( "--thread-model=MODEL" , "thread model" ) do |value |
270- options [ :thread_model ] = value
274+ opts . on (
275+ "--thread-model=MODEL" ,
276+ String ,
277+ "thread model (same as THREAD_MODEL=MODEL)" ,
278+ ) do |value |
279+ make_variables [ "THREAD_MODEL" ] = value
271280 end
272281 opts . on (
273282 "--scope=SCOPE" , [ :all , :core , :extensions ] ,
@@ -301,7 +310,16 @@ def parse_options(argv)
301310 options [ :verbose ] = true
302311 end
303312 end
304- inputs = parser . parse ( argv )
313+ inputs = [ ]
314+ rest = parser . order ( argv ) do |arg |
315+ if /\A ([A-Za-z_]\w *)=(.*)\z /m =~ arg
316+ make_variables [ $1] = $2
317+ else
318+ inputs << arg
319+ end
320+ end
321+ inputs . concat ( rest )
322+ options [ :make_variables ] = make_variables unless make_variables . empty?
305323 options [ :output ] = output if options [ :mode ] == :output
306324 [ options , inputs ]
307325 end
@@ -315,22 +333,55 @@ def execute(inputs, root: DEFAULT_ROOT, **options)
315333 # Creates a dependency updater for +root+.
316334 def initialize ( root : DEFAULT_ROOT )
317335 @root = File . expand_path ( root )
318- @thread_model = nil
336+ @make_variables = { }
319337 @dependency_declarations = { }
320338 @dependency_targets = { }
321339 @dependency_contents = { }
322340 end
323341
324342 private
325343
326- # Selects the thread model used to expand dependency declarations.
327- def select_thread_model ( thread_model )
328- return if @thread_model == thread_model
344+ # Selects known Make variable values used during dependency generation.
345+ def select_make_variables ( make_variables )
346+ make_variables = make_variables . to_h do |name , value |
347+ [ name . to_s , Array ( value ) . map ( &:to_s ) ]
348+ end
349+ return if @make_variables == make_variables
329350
330- @thread_model = thread_model
351+ @make_variables = make_variables
331352 @dependency_declarations . clear
332353 end
333354
355+ # Expands known Make variables embedded in +value+.
356+ def expand_make_variables ( value , defaults : { } )
357+ value . gsub ( /\$ \( (\w +)\) / ) do |variable |
358+ name = $1
359+ values = if @make_variables . key? ( name )
360+ @make_variables [ name ]
361+ elsif defaults . key? ( name )
362+ Array ( defaults [ name ] )
363+ else
364+ next variable
365+ end
366+ unless values . one?
367+ raise ArgumentError , "#{ name } is not a scalar Make variable"
368+ end
369+ values . first . to_s
370+ end
371+ end
372+
373+ # Expands known Make variables that stand for dependency lists.
374+ def expand_dependency_variables ( dependencies )
375+ dependencies . flat_map do |dependency |
376+ if /\A \$ \( (\w +)\) \z / =~ dependency &&
377+ ( values = @make_variables [ $1] )
378+ values . flat_map ( &:split )
379+ else
380+ expand_make_variables ( dependency )
381+ end
382+ end
383+ end
384+
334385 # Expands matching wildcards in a +scan+ declaration.
335386 #
336387 # Returns a generated-name to source-path mapping.
@@ -390,14 +441,10 @@ def parse_dependency_declarations(path, content = nil)
390441 end
391442 end
392443 declarations . scan . transform_values! do |source |
393- source . gsub ( '$(THREAD_MODEL)' , @thread_model || ' pthread' )
444+ expand_make_variables ( source , defaults : { "THREAD_MODEL" => " pthread" } )
394445 end
395- if @thread_model
396- declarations . dependencies . transform_values! do |dependencies |
397- dependencies . map do |dependency |
398- dependency . gsub ( '$(THREAD_MODEL)' , @thread_model )
399- end
400- end
446+ declarations . dependencies . transform_values! do |dependencies |
447+ expand_dependency_variables ( dependencies )
401448 end
402449 declarations
403450 end
@@ -705,9 +752,33 @@ def compact_dependencies(rules, group: true)
705752 lines . uniq . sort . join
706753 end
707754
755+ # Returns target and dependency pairs from Make rules.
756+ def dependency_pairs ( rules )
757+ rules = normalize_dependency_rules ( rules )
758+ rules . each_line . each_with_object ( Set . new ) do |line , pairs |
759+ next unless /\A (\S +(?:\s +\S +)*):\s *(.*?)\s *\z / =~ line
760+
761+ targets = $1. split
762+ dependencies = expand_dependency_variables ( $2. split ) . map do |dependency |
763+ normalize_dependency_rules ( dependency )
764+ end
765+ targets . product ( dependencies ) { |pair | pairs << pair }
766+ end
767+ end
768+
769+ # Removes generated dependencies already covered by +manual_rules+.
770+ def remove_manual_dependencies ( generated , manual_rules )
771+ manual = dependency_pairs ( manual_rules )
772+ generated . each_line . reject do |line |
773+ normalized = normalize_dependency_rules ( line )
774+ /\A (\S +):\s +(\S +)\s *\z / =~ normalized &&
775+ manual . include? ( [ $1, $2] )
776+ end . join
777+ end
778+
708779 # Removes VPATH markers that are unnecessary in build-directory output.
709780 def normalize_dependency_rules ( rules )
710- rules . gsub ( '{$ (VPATH)}' , '' )
781+ rules . gsub ( / \{ (?: \. ;)? \$ \ ( VPATH\) \} / , '' )
711782 end
712783
713784 # Returns whether two rule sets differ only by removable VPATH markers.
@@ -826,10 +897,13 @@ def update_deps(rules, input, group: true, verbose: false)
826897
827898 # Updates the marked dependency section in extension file +input+.
828899 #
829- # +source_map+ maps object targets to source paths. Returns +true+ when
830- # the file changed and +false+ when its dependencies were already current.
831- def update_extension ( input , source_map , nmake : false , verbose : false )
832- select_thread_model ( nil )
900+ # +source_map+ maps object targets to source paths. +make_variables+
901+ # supplies values of Make variables used by manual dependency rules.
902+ # Returns +true+ when the file changed and +false+ when its dependencies
903+ # were already current.
904+ def update_extension ( input , source_map , make_variables : { } , nmake : false ,
905+ verbose : false )
906+ select_make_variables ( make_variables )
833907 input = File . expand_path ( input )
834908 deps = File . read ( input ) if File . file? ( input )
835909 match = MARK_SECTION . match ( deps ) if deps
@@ -848,7 +922,9 @@ def update_extension(input, source_map, nmake: false, verbose: false)
848922 source , generated , target : target , input : input , project : true
849923 )
850924 end
851- expected = compact_dependencies ( generated . join , group : !nmake )
925+ manual = match . pre_match + match . post_match
926+ generated = remove_manual_dependencies ( generated . join , manual )
927+ expected = compact_dependencies ( generated , group : !nmake )
852928 updated = match . pre_match + expected + match . post_match
853929 return false if same_dependency_rules? ( match [ 0 ] , expected )
854930
@@ -896,12 +972,13 @@ def report_outdated_dependencies(input, current, expected, err: $stderr)
896972
897973 # Processes dependency +inputs+ according to the selected update +mode+.
898974 #
899- # +thread_model+ replaces <tt>$(THREAD_MODEL)</tt> in declarations.
975+ # +make_variables+ supplies values for Make variables in declarations.
976+ # +thread_model+ is a shortcut for its <tt>THREAD_MODEL</tt> entry.
900977 #
901978 # Returns +false+ only when check mode finds an outdated dependency file.
902979 def run ( inputs = ARGV , out : $stdout, err : $stderr, mode : :stdout ,
903- output : nil , nmake : false , sources : false , scope : nil ,
904- thread_model : nil , verbose : false )
980+ output : nil , make_variables : { } , nmake : false , sources : false ,
981+ scope : nil , thread_model : nil , verbose : false )
905982 case mode
906983 when :output
907984 raise ArgumentError , "output directory is missing" unless output
@@ -910,7 +987,13 @@ def run(inputs = ARGV, out: $stdout, err: $stderr, mode: :stdout,
910987 else
911988 raise ArgumentError , "unknown update mode: #{ mode . inspect } "
912989 end
913- select_thread_model ( thread_model )
990+ unless thread_model . nil?
991+ if thread_model . empty?
992+ raise ArgumentError , "thread model must not be empty"
993+ end
994+ make_variables = make_variables . merge ( "THREAD_MODEL" => thread_model )
995+ end
996+ select_make_variables ( make_variables )
914997 if scope
915998 inputs = dependency_files ( scope ) . map { |file | File . join ( @root , file ) }
916999 end
0 commit comments