forked from OSchip/llvm-project
252 lines
8.0 KiB
Ruby
Executable File
252 lines
8.0 KiB
Ruby
Executable File
#! /usr/bin/ruby
|
|
#
|
|
|
|
# A script to impose order on the Xcode project file, to make merging
|
|
# across branches were many additional files are present, easier.
|
|
|
|
|
|
|
|
|
|
## Sort the BuildFile and FileReference sections of an Xcode project file,
|
|
## putting Apple/github-local files at the front to avoid merge conflicts.
|
|
#
|
|
## Run this in a directory with a project.pbxproj file. The sorted version
|
|
## is printed on standard output.
|
|
#
|
|
|
|
|
|
# Files with these words in the names will be sorted into a separate section;
|
|
# they are only present in some repositories and so having them intermixed
|
|
# can lead to merge failures.
|
|
segregated_filenames = ["Swift", "repl", "RPC"]
|
|
|
|
def read_pbxproj(fn)
|
|
beginning = Array.new # All lines before "PBXBuildFile section"
|
|
files = Array.new # PBXBuildFile section lines -- sort these
|
|
middle = Array.new # All lines between PBXBuildFile and PBXFileReference sections
|
|
refs = Array.new # PBXFileReference section lines -- sort these
|
|
ending = Array.new # All lines after PBXFileReference section
|
|
|
|
all_lines = File.readlines fn
|
|
|
|
state = 1 # "begin"
|
|
all_lines.each do |l|
|
|
l.chomp
|
|
if state == 1 && l =~ /Begin PBXBuildFile section/
|
|
beginning.push(l)
|
|
state = 2
|
|
next
|
|
end
|
|
if state == 2 && l =~ /End PBXBuildFile section/
|
|
middle.push(l)
|
|
state = 3
|
|
next
|
|
end
|
|
if state == 3 && l =~ /Begin PBXFileReference section/
|
|
middle.push(l)
|
|
state = 4
|
|
next
|
|
end
|
|
if state == 4 && l =~ /End PBXFileReference section/
|
|
ending.push(l)
|
|
state = 5
|
|
next
|
|
end
|
|
|
|
if state == 1
|
|
beginning.push(l)
|
|
elsif state == 2
|
|
files.push(l)
|
|
elsif state == 3
|
|
middle.push(l)
|
|
elsif state == 4
|
|
refs.push(l)
|
|
else
|
|
ending.push(l)
|
|
end
|
|
end
|
|
|
|
return beginning, files, middle, refs, ending
|
|
end
|
|
|
|
xcodeproj_filename = nil
|
|
[ "../lldb.xcodeproj/project.pbxproj", "lldb.xcodeproj/project.pbxproj", "project.pbxproj" ].each do |ent|
|
|
if File.exists?(ent)
|
|
xcodeproj_filename = ent
|
|
break
|
|
end
|
|
end
|
|
|
|
if xcodeproj_filename.nil?
|
|
STDERR.puts "Could not find xcode project file to sort."
|
|
exit(1)
|
|
end
|
|
|
|
beginning, files, middle, refs, ending = read_pbxproj(xcodeproj_filename)
|
|
|
|
|
|
### If we're given a "canonical" project.pbxproj file, get the uuid and fileref ids for
|
|
### every source file in this project.pbxproj and the canonical one, and fix any of
|
|
### the identifiers that don't match in the project file we're updating.
|
|
### this comes up when people add the file independently on different branches and it
|
|
### gets different identifiers.
|
|
|
|
if ARGV.size() > 0
|
|
canonical_pbxproj = nil
|
|
if ARGV.size == 2 && ARGV[0] == "--canonical"
|
|
canonical_pbxproj = ARGV[1]
|
|
elsif ARGV.size == 1 && ARGV[0] =~ /--canonical=(.+)/
|
|
canonical_pbxproj = $1
|
|
end
|
|
|
|
if File.exists?(canonical_pbxproj)
|
|
ignore1, canon_files, ignore2, ignore3, ignore4 = read_pbxproj(canonical_pbxproj)
|
|
canon_files_by_filename = Hash.new { |k, v| k[v] = Array.new }
|
|
|
|
canon_files.each do |l|
|
|
# 2669421A1A6DC2AC0063BE93 /* MICmdCmdTarget.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 266941941A6DC2AC0063BE93 /* MICmdCmdTarget.cpp */; };
|
|
|
|
if l =~ /^\s+([A-F0-9]{24})\s+\/\*\s+(.*?)\sin.*?\*\/.*?fileRef = ([A-F0-9]{24})\s.*$/
|
|
uuid = $1
|
|
filename = $2
|
|
fileref = $3
|
|
canon_files_by_filename[filename].push({ :uuid => uuid, :fileref => fileref })
|
|
end
|
|
end
|
|
|
|
this_project_files = Hash.new { |k, v| k[v] = Array.new }
|
|
|
|
files.each do |l|
|
|
# 2669421A1A6DC2AC0063BE93 /* MICmdCmdTarget.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 266941941A6DC2AC0063BE93 /* MICmdCmdTarget.cpp */; };
|
|
|
|
if l =~ /^\s+([A-F0-9]{24})\s+\/\*\s+(.*?)\sin.*?\*\/.*?fileRef = ([A-F0-9]{24})\s.*$/
|
|
uuid = $1
|
|
filename = $2
|
|
fileref = $3
|
|
this_project_files[filename].push({ :uuid => uuid, :fileref => fileref })
|
|
end
|
|
end
|
|
|
|
this_project_files.keys.each do |fn|
|
|
next if !canon_files_by_filename.has_key?(fn)
|
|
next if this_project_files[fn].size() > 1 || canon_files_by_filename[fn].size() > 1
|
|
this_ent = this_project_files[fn][0]
|
|
canon_ent = canon_files_by_filename[fn][0]
|
|
if this_ent[:uuid] != canon_ent[:uuid]
|
|
STDERR.puts "#{fn} has uuid #{this_ent[:uuid]} in this project file, #{canon_ent[:uuid]} in the canonical"
|
|
[ beginning, files, middle, refs, ending ].each do |arr|
|
|
arr.each { |l| l.gsub!(this_ent[:uuid], canon_ent[:uuid]) }
|
|
end
|
|
end
|
|
if this_ent[:fileref] != canon_ent[:fileref]
|
|
STDERR.puts "#{fn} has fileref #{this_ent[:fileref]} in this project file, #{canon_ent[:fileref]} in the canonical"
|
|
[ beginning, files, middle, refs, ending ].each do |arr|
|
|
arr.each { |l| l.gsub!(this_ent[:fileref], canon_ent[:fileref]) }
|
|
end
|
|
end
|
|
|
|
end
|
|
end
|
|
end
|
|
|
|
|
|
|
|
######### Sort FILES by the filename, putting swift etc in front
|
|
|
|
# key is filename
|
|
# value is array of text lines for that filename in the FILES text
|
|
# (libraries like libz.dylib seem to occur multiple times, probably
|
|
# once each for different targets).
|
|
|
|
files_by_filename = Hash.new { |k, v| k[v] = Array.new }
|
|
|
|
files.each do |l|
|
|
# 2669421A1A6DC2AC0063BE93 /* MICmdCmdTarget.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 266941941A6DC2AC0063BE93 /* MICmdCmdTarget.cpp */; };
|
|
|
|
if l =~ /^\s+([A-F0-9]{24})\s+\/\*\s+(.*?)\sin.*?\*\/.*?fileRef = ([A-F0-9]{24})\s.*$/
|
|
uuid = $1
|
|
filename = $2
|
|
fileref = $3
|
|
files_by_filename[filename].push(l)
|
|
end
|
|
|
|
end
|
|
|
|
# clear the FILES array
|
|
|
|
files = Array.new
|
|
|
|
# add the lines in sorted order. First swift/etc, then everything else.
|
|
|
|
segregated_filenames.each do |keyword|
|
|
filenames = files_by_filename.keys
|
|
filenames.select {|l| l.include?(keyword) }.sort.each do |fn|
|
|
# re-add all the lines for the filename FN to our FILES array that we'll
|
|
# be outputting.
|
|
files_by_filename[fn].sort.each do |l|
|
|
files.push(l)
|
|
end
|
|
files_by_filename.delete(fn)
|
|
end
|
|
end
|
|
|
|
# All segregated filenames have been added to the FILES output array.
|
|
# Now add all the other lines, sorted by filename.
|
|
|
|
files_by_filename.keys.sort.each do |fn|
|
|
files_by_filename[fn].sort.each do |l|
|
|
files.push(l)
|
|
end
|
|
end
|
|
|
|
######### Sort REFS by the filename, putting swift etc in front
|
|
|
|
refs_by_filename = Hash.new { |k, v| k[v] = Array.new }
|
|
refs.each do |l|
|
|
# 2611FF12142D83060017FEA3 /* SBValue.i */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c.preprocessed; path = SBValue.i; sourceTree = "<group>"; };
|
|
|
|
if l =~ /^\s+([A-F0-9]{24})\s+\/\*\s+(.*?)\s\*\/.*$/
|
|
uuid = $1
|
|
filename = $2
|
|
refs_by_filename[filename].push(l)
|
|
end
|
|
end
|
|
|
|
# clear the refs array
|
|
|
|
refs = Array.new
|
|
|
|
# add the lines in sorted order. First swift/etc, then everything else.
|
|
|
|
|
|
segregated_filenames.each do |keyword|
|
|
filenames = refs_by_filename.keys
|
|
filenames.select {|l| l.include?(keyword) }.sort.each do |fn|
|
|
# re-add all the lines for the filename FN to our refs array that we'll
|
|
# be outputting.
|
|
refs_by_filename[fn].sort.each do |l|
|
|
refs.push(l)
|
|
end
|
|
refs_by_filename.delete(fn)
|
|
end
|
|
end
|
|
|
|
# All segregated filenames have been added to the refs output array.
|
|
# Now add all the other lines, sorted by filename.
|
|
|
|
refs_by_filename.keys.sort.each do |fn|
|
|
refs_by_filename[fn].sort.each do |l|
|
|
refs.push(l)
|
|
end
|
|
end
|
|
|
|
|
|
|
|
####### output the sorted pbxproj
|
|
|
|
File.open(xcodeproj_filename, 'w') do |outfile|
|
|
[ beginning, files, middle, refs, ending ].each do |arr|
|
|
arr.each {|l| outfile.puts l}
|
|
end
|
|
end
|