wxWidgets/build/osx/fix_xcode_ids.py
Vadim Zeitlin 3f66f6a5b3 Remove all lines containing cvs/svn "$Id$" keyword.
This keyword is not expanded by Git which means it's not replaced with the
correct revision value in the releases made using git-based scripts and it's
confusing to have lines with unexpanded "$Id$" in the released files. As
expanding them with Git is not that simple (it could be done with git archive
and export-subst attribute) and there are not many benefits in having them in
the first place, just remove all these lines.

If nothing else, this will make an eventual transition to Git simpler.

Closes #14487.

git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@74602 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
2013-07-26 16:02:46 +00:00

281 lines
10 KiB
Python
Executable File

#!/usr/bin/python
###############################################################################
# Name: build/osx/fix_xcode_ids.py
# Author: Dimitri Schoolwerth
# Created: 2010-09-08
# Copyright: (c) 2010 wxWidgets team
# Licence: wxWindows licence
###############################################################################
testFixStage = False
import os
import sys
import re
USAGE = """fix_xcode_ids - Modifies an Xcode project in-place to use the same identifiers (based on name) instead of being different on each regeneration"
Usage: fix_xcode_ids xcode_proj_dir"""
if not testFixStage:
if len(sys.argv) < 2:
print USAGE
sys.exit(1)
projectFile = sys.argv[1] + "/project.pbxproj"
fin = open(projectFile, "r")
strIn = fin.read()
fin.close()
# Xcode identifiers (IDs) consist of 24 hexadecimal digits
idMask = "[A-Fa-f0-9]{24}"
idDict = {}
# convert a name to an identifier for Xcode
def toUuid(name):
from uuid import uuid3, UUID
id = uuid3(UUID("349f853c-91f8-4eba-b9b9-5e9f882e693c"), name).hex[:24].upper()
# Some names can appear twice or even more (depending on number of
# targets), make them unique
while id in idDict.values() :
id = "%024X" % (int(id, 16) + 1)
return id
def insertBuildFileEntry(filePath, fileRefId):
global strIn
print "\tInsert PBXBuildFile for '%s'..." % filePath,
matchBuildFileSection = re.search("/\* Begin PBXBuildFile section \*/\n", strIn)
dirName, fileName = os.path.split(filePath)
fileInSources = fileName + " in Sources"
id = toUuid(fileInSources)
idDict[id] = id
insert = "\t\t%s /* %s */ = {isa = PBXBuildFile; fileRef = %s /* %s */; };\n" % (id, fileInSources, fileRefId, fileName)
strIn = strIn[:matchBuildFileSection.end()] + insert + strIn[matchBuildFileSection.end():]
print "OK"
return id
def insertFileRefEntry(filePath, id = 0):
global strIn
print "\tInsert PBXFileReference for '%s'..." % filePath,
matchFileRefSection = re.search("/\* Begin PBXFileReference section \*/\n", strIn)
dirName, fileName = os.path.split(filePath)
if id == 0:
id = toUuid(fileName)
idDict[id] = id
insert = "\t\t%s /* %s */ = {isa = PBXFileReference; lastKnownFileType = file; name = %s; path = %s; sourceTree = \"<group>\"; };\n" % (id, fileName, fileName, filePath)
strIn = strIn[:matchFileRefSection.end()] + insert + strIn[matchFileRefSection.end():]
print "OK"
return id
def insertSourcesBuildPhaseEntry(id, fileName, insertBeforeFileName, startSearchPos = 0):
global strIn
print "\tInsert PBXSourcesBuildPhase for '%s'..." % fileName,
matchBuildPhase = re.compile(".+ /\* " + insertBeforeFileName + " in Sources \*/,") \
.search(strIn, startSearchPos)
insert = "\t\t\t\t%s /* %s in Sources */,\n" % (id, fileName)
strIn = strIn[:matchBuildPhase.start()] \
+ insert \
+ strIn[matchBuildPhase.start():]
print "OK"
return matchBuildPhase.start() + len(insert) + len(matchBuildPhase.group(0))
# Detect and fix errors in the project file that might have been introduced.
# Sometimes two source files are concatenated. These are spottable by
# looking for patterns such as "filename.cppsrc/html/"
# Following is a stripped Xcode project containing several problems that
# are solved after finding the error.
strTest = \
"""/* Begin PBXBuildFile section */
95DE8BAB1238EE1800B43069 /* m_fonts.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 95DE8BAA1238EE1700B43069 /* m_fonts.cpp */; };
95DE8BAC1238EE1800B43069 /* m_fonts.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 95DE8BAA1238EE1700B43069 /* m_fonts.cpp */; };
/* End PBXBuildFile section */
/* Begin PBXFileReference section */
95DE8BAA1238EE1700B43069 /* m_fonts.cpp */ = {isa = PBXFileReference; lastKnownFileType = file; name = m_fonts.cpp; path = ../../src/html/m_dflist.cppsrc/html/m_fonts.cpp; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXGroup section */
95DE8B831238EE1000B43069 /* html */ = {
isa = PBXGroup;
children = (
95DE8B841238EE1000B43069 /* src/html */,
95DE8BA91238EE1700B43069 /* src/html/m_dflist.cppsrc/html */,
95DE8BCE1238EE1F00B43069 /* src/generic */,
);
name = html;
sourceTree = "<group>";
};
95DE8B841238EE1000B43069 /* src/html */ = {
isa = PBXGroup;
children = (
95DE8B851238EE1000B43069 /* chm.cpp */,
95DE8BAD1238EE1800B43069 /* m_hline.cpp */,
);
name = src/html;
sourceTree = "<group>";
};
95DE8BA91238EE1700B43069 /* src/html/m_dflist.cppsrc/html */ = {
isa = PBXGroup;
children = (
95DE8BAA1238EE1700B43069 /* m_fonts.cpp */,
);
name = src/html/m_dflist.cppsrc/html;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXSourcesBuildPhase section */
404BEE5E10EC83280080E2B8 /* Sources */ = {
files = (
95DE8BAC1238EE1800B43069 /* m_fonts.cpp in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
D2AAC0C405546C1D00DB518D /* Sources */ = {
files = (
95DE8BAB1238EE1800B43069 /* m_fonts.cpp in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */"""
if testFixStage:
strIn = strTest
rc = re.compile(".+ (?P<path1>[\w/.]+(\.cpp|\.cxx|\.c))(?P<path2>\w+/[\w/.]+).+")
matchLine = rc.search(strIn)
while matchLine:
line = matchLine.group(0)
# is it a line from the PBXFileReference section containing 2 mixed paths?
# example:
# FEDCBA9876543210FEDCBA98 /* file2.cpp */ = {isa = PBXFileReference; lastKnownFileType = file; name = file2.cpp; path = ../../src/html/file1.cppsrc/html/file2.cpp; sourceTree = "<group>"; };
if line.endswith("};") :
path1 = matchLine.group('path1')
path2 = matchLine.group('path2')
print "Correcting mixed paths '%s' and '%s' at '%s':" % (path1, path2, line)
# if so, make note of the ID used (belongs to path2), remove the line
# and split the 2 paths inserting 2 new entries inside PBXFileReference
fileRefId2 = re.search(idMask, line).group(0)
print "\tDelete the offending PBXFileReference line...",
# delete the PBXFileReference line that was found and which contains 2 mixed paths
strIn = strIn[:matchLine.start()] + strIn[matchLine.end()+1:]
print "OK"
# insert corrected path1 entry in PBXFileReference
fileRefId1 = insertFileRefEntry(path1)
# do the same for path2 (which already had a ID)
path2Corrected = path2
if path2Corrected.startswith('src') :
path2Corrected = '../../' + path2Corrected
insertFileRefEntry(path2Corrected, fileRefId2)
buildPhaseId = {}
# insert a PBXBuildFile entry, 1 for each target
# path2 already has correct PBXBuildFile entries
targetCount = strIn.count("isa = PBXSourcesBuildPhase")
for i in range(0, targetCount):
buildPhaseId[i] = insertBuildFileEntry(path1, fileRefId1)
fileName1 = os.path.split(path1)[1]
dir2, fileName2 = os.path.split(path2)
# refer to each PBXBuildFile in each PBXSourcesBuildPhase
startSearchIndex = 0
for i in range(0, targetCount):
startSearchIndex = insertSourcesBuildPhaseEntry(buildPhaseId[i], fileName1, fileName2, startSearchIndex)
# insert both paths in the group they belong to
matchGroupStart = re.search("/\* %s \*/ = {" % dir2, strIn)
endGroupIndex = strIn.find("};", matchGroupStart.start())
for matchGroupLine in re.compile(".+" + idMask + " /\* (.+) \*/,").finditer(strIn, matchGroupStart.start(), endGroupIndex) :
if matchGroupLine.group(1) > fileName1:
print "\tInsert paths in PBXGroup '%s', just before '%s'..." % (dir2, matchGroupLine.group(1)),
strIn = strIn[:matchGroupLine.start()] \
+ "\t\t\t\t%s /* %s */,\n" % (fileRefId1, fileName1) \
+ "\t\t\t\t%s /* %s */,\n" % (fileRefId2, fileName2) \
+ strIn[matchGroupLine.start():]
print "OK"
break
elif line.endswith("*/ = {") :
print "Delete invalid PBXGroup starting at '%s'..." % line,
find = "};\n"
endGroupIndex = strIn.find(find, matchLine.start()) + len(find)
strIn = strIn[:matchLine.start()] + strIn[endGroupIndex:]
print "OK"
elif line.endswith(" */,") :
print "Delete invalid PBXGroup child '%s'..." % line,
strIn = strIn[:matchLine.start()] + strIn[matchLine.end()+1:]
print "OK"
matchLine = rc.search(strIn)
if testFixStage:
print "------------------------------------------"
print strIn
exit(1)
# key = original ID found in project
# value = ID it will be replaced by
idDict = {}
# some of the strings to match to find definitions of Xcode IDs:
# from PBXBuildFile section:
# 0123456789ABCDEF01234567 /* filename.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FEDCBA9876543210FEDCBA98 /* filename.cpp */; };
# from PBXFileReference section:
# FEDCBA9876543210FEDCBA98 /* filename.cpp */ = {isa = PBXFileReference; lastKnownFileType = file; name = any.cpp; path = ../../src/common/filename.cpp; sourceTree = "<group>"; };
# from remaining sections:
# 890123456789ABCDEF012345 /* Name */ = {
# Capture the first comment between /* and */ (file/section name) as a group
rc = re.compile("\s+(" + idMask + ") /\* (.+) \*/ = {.*$", re.MULTILINE)
dict = rc.findall(strIn)
for s in dict:
# s[0] is the original ID, s[1] is the name
assert(not s[0] in idDict)
idDict[s[0]] = toUuid(s[1])
# replace all found identifiers with the new ones
def repl(match):
return idDict[match.group(0)]
strOut = re.sub(idMask, repl, strIn)
fout = open(projectFile, "w")
fout.write(strOut)
fout.close()