From f05b935882198ccf7d81675736e3aeb089c5113a Mon Sep 17 00:00:00 2001 From: Ben Murdoch Date: Thu, 5 May 2011 14:36:32 +0100 Subject: Merge WebKit at r74534: Initial merge by git. Change-Id: I6ccd1154fa1b19c2ec2a66878eb675738735f1eb --- Tools/android/webkitmerge/webkitmerge.cpp | 1834 +++++++++++++++++++++++++++++ 1 file changed, 1834 insertions(+) create mode 100644 Tools/android/webkitmerge/webkitmerge.cpp (limited to 'Tools/android/webkitmerge/webkitmerge.cpp') diff --git a/Tools/android/webkitmerge/webkitmerge.cpp b/Tools/android/webkitmerge/webkitmerge.cpp new file mode 100644 index 0000000..22eeb54 --- /dev/null +++ b/Tools/android/webkitmerge/webkitmerge.cpp @@ -0,0 +1,1834 @@ +/* + * Copyright 2009, The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include + +using namespace std; + +string WEBKITLIB = "external/webkit"; + +string oldBaseStr; +string oldCmdStr; +string newBaseStr; +string newCmdStr; +string sandboxBaseStr; +string sandboxCmdStr; +string outputDir; +string scratchDir; + +const char* oldBase; +const char* oldCmd; +const char* newBase; +const char* newCmd; +const char* sandboxBase; +const char* sandboxCmd; + +bool assert_debug; + +#define myassert(a) do { \ + if (!(a)) { \ + fprintf(stderr, "%s %d %s\n", __FUNCTION__, __LINE__, #a); \ + fflush(stderr); \ + if (assert_debug) for(;;); else exit(0); \ + } \ +} while(false) + +class Options { +public: + Options() : emitGitCommands(false), emitPerforceCommands(false), + mergeMake(true), copyOther(true), mergeCore(true), + removeEmptyDirs(true), removeSVNDirs(true), debug(false), + execute(false), verbose(false), cleared(false) + { + } + + bool finish(); + void clearOnce() + { + if (cleared) + return; + mergeMake = copyOther = mergeCore = removeEmptyDirs = removeSVNDirs = false; + cleared = true; + } + string androidWebKit; + string baseWebKit; + string newWebKit; + bool emitGitCommands; + bool emitPerforceCommands; + bool mergeMake; + bool copyOther; + bool mergeCore; + bool removeEmptyDirs; + bool removeSVNDirs; + bool debug; + bool execute; + bool verbose; +private: + bool cleared; +}; + +Options options; + +char* GetFile(string fileNameStr, size_t* sizePtr = NULL, int* lines = NULL) +{ + const char* fileName = fileNameStr.c_str(); + FILE* file = fopen(fileName, "r"); + if (file == NULL) + { + fprintf(stderr, "can't read %s\n", fileName); + myassert(0); + return 0; + } + fseek(file, 0, SEEK_END); + size_t size = ftell(file); + if (sizePtr) + *sizePtr = size; + fseek(file, 0, SEEK_SET); + char* buffer = new char[size + 2]; + fread(buffer, size, 1, file); + buffer[size] = buffer[size + 1] = '\0'; + int lineCount = 0; + for (size_t index = 0; index < size; index++) { + if (buffer[index] == '\n') { + buffer[index] = '\0'; + lineCount++; + } + } + if (lines) + *lines = lineCount; + fclose(file); + return buffer; +} + +bool Options::finish() +{ + ::assert_debug = options.debug; + if (androidWebKit.size() == 0) { + fprintf(stderr, "missing --android parameter"); + return false; + } + if (baseWebKit.size() == 0) { + fprintf(stderr, "missing --basewebkit parameter"); + return false; + } + if (newWebKit.size() == 0) { + fprintf(stderr, "missing --newwebkit parameter"); + return false; + } + sandboxBaseStr = androidWebKit + "/" + WEBKITLIB; + sandboxCmdStr = sandboxBaseStr; + int err = system("pwd > pwd.txt"); + myassert(err != -1); + outputDir = string(GetFile("pwd.txt")); + system("rm pwd.txt"); + myassert(outputDir.size() > 0); + scratchDir = outputDir; + outputDir += "/scripts/"; + string outputMkdir = "test -d " + outputDir + " || mkdir " + outputDir; + system(outputMkdir.c_str()); + scratchDir += "/scratch/"; + string scratchMkdir = "test -d " + scratchDir + " || mkdir " + scratchDir; + system(scratchMkdir.c_str()); + oldBaseStr = baseWebKit; + oldCmdStr = oldBaseStr; + newBaseStr = newWebKit; + newCmdStr = newBaseStr; + oldBase = oldBaseStr.c_str(); + oldCmd = oldCmdStr.c_str(); + newBase = newBaseStr.c_str(); + newCmd = newCmdStr.c_str(); + sandboxBase = sandboxBaseStr.c_str(); + sandboxCmd = sandboxCmdStr.c_str(); + return true; +} + +// scratch files +string ScratchFile(const char* name) +{ + return scratchDir + name + ".txt"; +} + +FILE* commandFile; +FILE* copyDirFile; +FILE* oopsFile; + +string SedEscape(const char* str) +{ + string result; + char ch; + while ((ch = *str++) != '\0') { + if (ch == '/' || ch == '\\' || ch == '$') + result += '\\'; + result += ch; + } + return result; +} + +char* List(const char* base, char* name, const char* workingDir) +{ + string listStr = "ls -F \""; + listStr += string(base) + "/" + workingDir + "\" > " + ScratchFile(name); + int err = system(listStr.c_str()); + myassert(err == 0); + return GetFile(ScratchFile(name)); +} + +bool Merge(const char* oldDir, const char* oldFile, const char* newDir, const char* newFile, + const char* outFile) +{ + char scratch[2048]; + + sprintf(scratch, "merge -p -q \"%s/%s/%s\" \"%s/%s/%s\" \"%s/%s/%s\" > %s", + sandboxBase, oldDir, oldFile, oldBase, oldDir, oldFile, newBase, newDir, newFile, outFile); + int err = system(scratch); + myassert(err == 0 || err ==1 || err == 256); + return err == 0; +} + +bool Merge(const char* dir, const char* file) +{ + return Merge(dir, file, dir, file, "/dev/null"); +} + +/* +static const char* skipNonSpace(char** linePtr) { + char* line = *linePtr; + while (line[0] && isspace(line[0]) == false) + line++; + *linePtr = line; + return line; +} +*/ + +static bool endsWith(const char* str, const char* end) { + size_t endLen = strlen(end); + const char* endStr = str + strlen(str) - endLen; + return endStr >= str && strcmp(endStr, end) == 0; +} + +static void skipSpace(char** linePtr) { + char* line = *linePtr; + while (isspace(line[0])) + line++; + *linePtr = line; +} + +/* +static void setTrimmed(string& str, char* loc, int len) { + char* start = loc; + skipSpace(&loc); + len -= loc - start; + while (len > 0 && isspace(loc[len - 1])) + len--; + str = string(loc, len); +} +*/ + +static bool skipText(char** linePtr, const char* text) { + skipSpace(linePtr); + size_t length = strlen(text); + bool result = strncmp(*linePtr, text, length) == 0; + if (result) + *linePtr += length; + skipSpace(linePtr); + return result; +} + +static bool isTokenChar(char ch) { + return isalnum(ch) || ch == '_'; +} + +struct Pair { + char* name; + int brace; +}; + +class Parse { +public: + char* m_text; + bool m_inComment; + bool m_inFunction; + bool m_inFindFunctionType; + vector m_classes; + vector m_namespaces; + vector m_preprocessorConditions; + string m_functionName; + string m_functionDeclaration; + string m_classDeclaration; + int m_braceLevel; + int m_functionBrace; + + Parse(char* text) : m_text(text), m_inComment(false), m_inFunction(false), m_inFindFunctionType(false), + m_braceLevel(0), m_functionBrace(0) {} + + int CheckForBrace() + { + int indent = 0; + // don't count braces in strings, chars, comments + do { + char* openBrace = strchr(m_text, '{'); + char* closeBrace = strchr(m_text, '}'); + if (openBrace == NULL && closeBrace == NULL) + break; + char* brace = openBrace == NULL ? closeBrace : closeBrace == NULL ? openBrace : + openBrace < closeBrace ? openBrace : closeBrace; + char* doubleQ = strchr(m_text, '"'); + char* singleQ = strchr(m_text, '\''); + char* quote = doubleQ == NULL ? singleQ : singleQ == NULL ? doubleQ : + doubleQ < singleQ ? doubleQ : singleQ; + char quoteMark = quote == doubleQ ? '"' : '\''; + if (quote && quote < brace) { + myassert(quote[-1] != '\\'); + do { + quote = strchr(quote + 1, quoteMark); + myassert(doubleQ); + } while (quote[-1] == '\\'); + m_text = quote + 1; + continue; + } + indent += openBrace != NULL ? 1 : -1; + m_text = openBrace + 1; + } while (m_text[0] != '\0'); + return indent; + } + +int ParseLine() +{ + size_t textLen = strlen(m_text); + char* lineStart = m_text; + char* commentStart; + if (m_text[0] == '\0') + goto nextLine; + if (m_inComment == false && m_text[0] == '/') { + m_inComment = m_text[1] == '*'; + if (m_text[1] == '/' || m_text[1] == '*') + goto nextLine; + } + commentStart = m_text; // before anything else, turn embedded comments into their own lines + if (m_inComment == false && skipText(&commentStart, "//")) { + if (commentStart < lineStart + textLen) + commentStart[0] = '/'; + else + commentStart[-1] = ' '; + commentStart -= 2; + commentStart[0] = '\0'; + textLen = commentStart - lineStart; + goto nextLine; + } + if (m_inComment || skipText(&commentStart, "/*")) { + char* commentEnd = commentStart; + if (skipText(&commentEnd, "*/")) { + if (commentEnd < lineStart + textLen) { + commentEnd[-1] = '\0'; + textLen = commentEnd - lineStart - 2; + } + if (m_inComment) { + m_inComment = false; + goto nextLine; + } + } + if (m_inComment) + goto nextLine; + memcpy(commentStart - 2, "\0/*", 3); + textLen = commentStart - lineStart - 2; + } + if (skipText(&m_text, "#include")) + goto nextLine; + if (skipText(&m_text, "#if") || skipText(&m_text, "#else")) { + Pair condition = { lineStart, m_braceLevel }; + m_preprocessorConditions.push_back(condition); + goto nextLine; + } + { + bool is_endif = false; + if (skipText(&m_text, "#elif") || (is_endif = skipText(&m_text, "#endif")) != false) { // pop prior elif, if + char* lastText; + do { + int last = m_preprocessorConditions.size() - 1; + myassert(last >= 0); + lastText = m_preprocessorConditions[last].name; + m_preprocessorConditions.pop_back(); + } while (is_endif && skipText(&lastText, "#else") == true); + goto nextLine; + } + } + if (skipText(&m_text, "namespace")) { + Pair space = { lineStart, m_braceLevel }; + m_namespaces.push_back(space); + goto checkForBrace; + } + if (m_inFunction) + goto checkForBrace; + // detect functions by looking for token preceding open-paren. + if (m_inFindFunctionType == false) { + char* openParen = strchr(m_text, '('); + if (openParen) { + char* last = openParen; + while (last > m_text && isspace(last[-1])) + --last; + while (last > m_text && ((isTokenChar(last[-1]) || last[-1] == ':') && last[-2] == ':')) + --last; + myassert(isdigit(last[0]) == false); + if (isTokenChar(last[0])) { + m_inFindFunctionType = true; + m_functionName = string(last); + } + } + } + { + char* openBrace = strchr(m_text, '{'); + char* semiColon = strchr(m_text, ';'); + if (semiColon != NULL && semiColon < openBrace) + openBrace = NULL; + // functions are of the form: type (class::)*function(parameter-list) { + // all of which may be on separate lines + // so keep track of returntype, class, function, paramlist, openbrace separately + if (m_inFindFunctionType == true) { // look ahead to see which comes first, a semicolon or an open brace + if (openBrace) { + m_functionBrace = ++m_braceLevel; + m_inFunction = true; + m_inFindFunctionType = false; + m_text = openBrace + 1; + goto checkForBrace; + } + if (semiColon != NULL) { // a function declaration + m_inFindFunctionType = false; + m_functionDeclaration = m_functionName; + m_functionName.erase(0, m_functionName.length()); + } else + goto nextLine; + } + // FIXME what if class line has neither brace nor semi? + if (skipText(&m_text, "class")) { + if (openBrace > m_text) { + Pair _class = { lineStart, m_braceLevel }; + m_classes.push_back(_class); + } else if (semiColon != NULL) { + m_classDeclaration = lineStart; // !!! FIXME should have function form as above + } + } + } +checkForBrace: + m_braceLevel += CheckForBrace(); + if (m_functionBrace > 0 && m_braceLevel <= m_functionBrace) { + m_functionName.erase(0, m_functionName.length()); + m_functionBrace = 0; + } +nextLine: + return textLen; +} + +}; + +char* const GetAndroidDiffs(const char* dir, const char* filename) +{ + char scratch[2048]; + string diffsFile = ScratchFile(__FUNCTION__); + sprintf(scratch, "diff \"%s/%s/%s\" \"%s/%s/%s\" > %s", sandboxBase, dir, + filename, oldBase, dir, filename, diffsFile.c_str()); + int err = system(scratch); + myassert(err == 0 || err == 256); + char* const diffs = GetFile(diffsFile); + return diffs; +} + +void CheckForExec(const char* base1, const char* dir1, const char* file1, + const char* base2, const char* dir2, const char* file2, bool* ex1, bool* ex2) +{ + size_t file1Len = strlen(file1); + size_t file2Len = strlen(file2); + bool file1Ex = file1[file1Len - 1] == '*'; + bool file2Ex = file2[file2Len - 1] == '*'; + if (file1Ex != file2Ex) { + fprintf(stderr, "warning: %s/%s/%s has %sexec bit set while" + " %s/%s/%s has %sexec bit set\n", + base1, dir1, file1, file1Ex ? "" : "no ", + base2, dir2, file2, file2Ex ? "" : "no "); + } + if (ex1) *ex1 = file1Ex; + if (ex2) *ex2 = file2Ex; +} + +bool CompareFiles(const char* base1, const char* dir1, const char* file1, + const char* base2, const char* dir2, const char* file2) +{ + char scratch[2048]; + bool file1Ex, file2Ex; + string compareFileStr = ScratchFile(__FUNCTION__); + CheckForExec(base1, dir1, file1, base2, dir2, file2, &file1Ex, &file2Ex); + sprintf(scratch, "diff --brief \"%s/%s/%.*s\" \"%s/%s/%.*s\" > %s", + base1, dir1, (int) strlen(file1) - (int) file1Ex, file1, + base2, dir2, (int) strlen(file2) - (int) file2Ex, file2, + compareFileStr.c_str()); + int err = system(scratch); + myassert(err == 0 || err == 256); + char* scratchText = GetFile(compareFileStr); + size_t len = strlen(scratchText); + delete[] scratchText; + return len > 0; +} + +bool CompareFiles(const char* base1, const char* base2, const char* dir, const char* file) +{ + return CompareFiles(base1, dir, file, base2, dir, file); +} + +int Compare(char* one, size_t len1, char* two, size_t len2) +{ + char* o_end = one + len1; + char* t_end = two + len2; + do { + if (one == o_end) + return two == t_end ? 0 : 1; + if (two == t_end) + return -1; + char o = *one++; + char t = *two++; + if (o == t) + continue; + if (o == '/') + return -1; + if (t == '/') + return 1; + return o < t ? -1 : 1; + } while (true); + myassert(0); + return 0; +} + +string Find(const char* oldList) +{ + string result; + char scratch[2048]; + // look in WebCore and JavaScriptCore + string findWebCore = ScratchFile("FindWebCore"); + sprintf(scratch, "cd %s%s ; find . -name \"%s\" > %s", + newBase, "/WebCore", oldList, findWebCore.c_str()); + int err = system(scratch); + myassert(err == 0 || err == 256); + int webCount; + char* foundInWebCore = GetFile(findWebCore, NULL, &webCount); + char* originalFoundInWebCore = foundInWebCore; + string findJavaScriptCore = ScratchFile("FindJavaScriptCore"); + sprintf(scratch, "cd %s%s ; find . -name \"%s\" > %s", + newBase, "/JavaScriptCore", oldList, findJavaScriptCore.c_str()); + err = system(scratch); + myassert(err == 0 || err == 256); + int javaScriptCount; + char* foundInJavaScriptCore = GetFile(findJavaScriptCore, NULL, &javaScriptCount); + char* originalFoundInJavaScriptCore = foundInJavaScriptCore; + if (webCount == 1 && javaScriptCount == 0) { + result = "WebCore/" + string(&foundInWebCore[2]); + } else if (webCount == 0 && javaScriptCount == 1) { + result = "JavaScriptCore/" + string(&foundInJavaScriptCore[2]); + } else if (webCount == 1 && javaScriptCount == 1 && + strncmp(&foundInWebCore[2], "ForwardingHeaders/", 18) == 0) { + result = "JavaScriptCore/" + string(&foundInJavaScriptCore[2]); + } else if (webCount == 1 && javaScriptCount == 1 && + strncmp(&foundInJavaScriptCore[2], "API/tests/", 10) == 0) { + result = "JavaScriptCore/" + string(&foundInJavaScriptCore[2]); + } else if (webCount + javaScriptCount > 0) { + fprintf(stderr, "deleted file \"%s\" has more than one possible rename:\n", oldList); + int index; + for (index = 0; index < webCount; index++) { + fprintf(stderr, "WebCore/%s\n", &foundInWebCore[2]); + foundInWebCore += strlen(foundInWebCore) + 1; + } + for (index = 0; index < javaScriptCount; index++) { + fprintf(stderr, "JavaScriptCore/%s\n", &foundInJavaScriptCore[2]); + foundInJavaScriptCore += strlen(foundInJavaScriptCore) + 1; + } + } + delete[] originalFoundInWebCore; + delete[] originalFoundInJavaScriptCore; + return result; +} + +char* GetMakeAndExceptions(const char* dir, const char* filename, size_t* makeSize, + string* excludedFilesPtr, string* excludedGeneratedPtr, + string* excludedDirsPtr, string* androidFilesPtr, + char** startPtr, char** localStartPtr) +{ + char scratch[1024]; + sprintf(scratch, "%s/%s/%s", sandboxBase, dir, filename); + char* makeFile = GetFile(scratch, makeSize); + char* start = makeFile; + do { // find first filename in makefile + if (strncmp(start, "# LOCAL_SRC_FILES_EXCLUDED := \\", 30) == 0) + break; + start += strlen(start) + 1; + } while (start < makeFile + *makeSize); + myassert(start[0] != '\0'); + start += strlen(start) + 1; + // construct one very large regular expression that looks like: + // echo '%s' | grep -v -E 'DerivedSources.cpp|WebCorePrefix.cpp...' + // to filter out matches that aren't allowed + // wildcards '*' in the original need to be expanded to '.*' + string excludedFiles = "grep -v -E '"; + while (strncmp(start, "#\t", 2) == 0) { + start += 2; + char ch; + while ((ch = *start++) != ' ') { + if (ch == '*') + excludedFiles += '.'; + else if (ch == '.') + excludedFiles += '\\'; + excludedFiles += ch; + } + excludedFiles += "|"; + myassert(*start == '\\'); + start += 2; + } + excludedFiles[excludedFiles.size() - 1] = '\''; + *excludedFilesPtr = excludedFiles; + do { + if (strncmp(start, "# LOCAL_GENERATED_FILES_EXCLUDED := \\", 37) == 0 || + strncmp(start, "# LOCAL_DIR_WILDCARD_EXCLUDED := \\", 34) == 0) + break; + start += strlen(start) + 1; + } while (start < makeFile + *makeSize); + if (strncmp(start, "# LOCAL_GENERATED_FILES_EXCLUDED := \\", 37) == 0) { + string excludedGenerated = "grep -v -E '"; + start += strlen(start) + 1; + while (strncmp(start, "#\t", 2) == 0) { + start += 2; + char ch; + while ((ch = *start++) != ' ') { + if (ch == '*') + excludedGenerated += '.'; + else if (ch == '.') + excludedGenerated += '\\'; + excludedGenerated += ch; + } + excludedGenerated += "|"; + myassert(*start == '\\'); + start += 2; + } + myassert(excludedGeneratedPtr); + excludedGenerated[excludedGenerated.size() - 1] = '\''; + *excludedGeneratedPtr = excludedGenerated; + } + do { // find first filename in makefile + if (strncmp(start, "# LOCAL_DIR_WILDCARD_EXCLUDED := \\", 34) == 0) + break; + start += strlen(start) + 1; + } while (start < makeFile + *makeSize); + if (start[0] != '\0') { + string excludedDirs = "-e '/\\.vcproj\\// d' -e '/\\.svn\\// d' "; + do { + start += strlen(start) + 1; + char* exceptionDirStart = start; + if (strncmp(exceptionDirStart, "#\t", 2) != 0) { + myassert(exceptionDirStart[0] == '\0'); + break; + } + exceptionDirStart += 2; + char* exceptionDirEnd = exceptionDirStart; + do { + exceptionDirEnd = strchr(exceptionDirEnd, '\\'); + } while (exceptionDirEnd && *++exceptionDirEnd == '/'); + myassert(exceptionDirEnd); + --exceptionDirEnd; + myassert(exceptionDirEnd[-1] == ' '); + myassert(exceptionDirEnd[-2] == '*'); + myassert(exceptionDirEnd[-3] == '/'); + exceptionDirEnd[-3] = '\0'; + excludedDirs += "-e '/"; + if (exceptionDirStart[0] == '/') + excludedDirs += "\\"; + excludedDirs += exceptionDirStart; + excludedDirs += "\\// d' "; + start = exceptionDirEnd; + } while (true); + *excludedDirsPtr = excludedDirs; + } + *startPtr = start; + // optionally look for android-specific files + char* makeEnd = makeFile + *makeSize; + do { // find first filename in makefile + if (strcmp(start, "# LOCAL_ANDROID_SRC_FILES_INCLUDED := \\") == 0) + break; + } while ((start += strlen(start) + 1), start < makeEnd); + if (start >= makeEnd) + return makeFile; + start += strlen(start) + 1; + string androidFiles = "grep -v -E '"; + do { + myassert(strncmp(start, "#\t", 2) == 0); + start += 2; + char ch; + bool isIdl = strstr(start, "idl \\") != 0; + char* lastSlash = strrchr(start, '/') + 1; + while ((ch = *start++) != ' ') { + if (ch == '*') + androidFiles += '.'; + else if (ch == '.') + androidFiles += '\\'; + androidFiles += ch; + if (!isIdl) + continue; + if (ch == '/' && start == lastSlash) + androidFiles += "JS"; + if (ch == '.') { + myassert(strcmp(start, "idl \\") == 0); + start += 4; + androidFiles += 'h'; + break; + } + } + androidFiles += "|"; + myassert(*start == '\\'); + start += 2; + } while (start[0] == '#'); + androidFiles[androidFiles.size() - 1] = '\''; + *androidFilesPtr = androidFiles; + return makeFile; +} + +vector GetDerivedSourcesMake(const char* dir) +{ + vector result; + char scratch[1024]; + sprintf(scratch, "%s/%s/%s", newBase, dir, "DerivedSources.make"); + size_t fileSize; + char* file = GetFile(scratch, &fileSize); + char* fileEnd = file + fileSize; + myassert(file); + size_t len; + do { // find first filename in makefile + len = strlen(file); + if (strcmp(file, "all : \\") == 0) + break; + file += len + 1; + } while (file < fileEnd); + myassert(strcmp(file, "all : \\") == 0); + file += len + 1; + while (file[0] != '#') { + len = strlen(file); + char* st = file; + skipSpace(&st); + if (st[0] != '\\' && st[0] != '$') + result.push_back(st); + file += len + 1; + } + return result; +} + +bool MarkDerivedFound(vector& derived, char* check, size_t len) +{ + bool found = false; + for (unsigned index = 0; index < derived.size(); index++) { + char* der = derived[index]; + if (strncmp(der, check, len) == 0) { + der[0] = '\0'; + found = true; + } + } + return found; +} + +void UpdateDerivedMake() +{ + const char* dir = "WebCore"; + int err; + vector derived = GetDerivedSourcesMake(dir); + size_t makeSize; + char* start, * localStart; + string excludedDirs, excludedFiles, excludedGenerated, androidFiles; + char* makeFile = GetMakeAndExceptions(dir, "Android.derived.mk", &makeSize, + &excludedFiles, &excludedGenerated, &excludedDirs, &androidFiles, + &start, &localStart); + if (options.emitPerforceCommands) + fprintf(commandFile, "p4 edit %s/%s/%s\n", sandboxCmd, dir, "Android.derived.mk"); + fprintf(commandFile, "cat %s/%s/%s | sed \\\n", sandboxCmd, dir, "Android.derived.mk"); + string updateDerivedMake = ScratchFile(__FUNCTION__); + string filelist = string("cd ") + newBase + "/" + dir + + " ; find . -name '*.idl' | " + excludedFiles + + " | sed -e 's/.\\///' " + excludedDirs + + " | sed 's@\\(.*\\)/\\(.*\\)\\.idl@ $(intermediates)/\\1/JS\\2.h@' " + " | sort -o " + updateDerivedMake; + err = system(filelist.c_str()); + myassert(err == 0 || err == 256); + char* bindings = GetFile(updateDerivedMake); + bool inGen = false; + char* fileEnd = makeFile + makeSize; + char* nextStart; + do { + size_t startLen = strlen(start); + nextStart = start + startLen + 1; + bool onGen = false; + char* st = start; + if (inGen == false) { + if (strncmp(st, "GEN", 3) != 0) + continue; + st += 3; + skipSpace(&st); + if (strncmp(st, ":=", 2) != 0) + continue; + st += 2; + onGen = true; + } + skipSpace(&st); + inGen = start[startLen - 1] == '\\'; + if (inGen) { + if (st[0] == '\\' && st[1] == '\0') + continue; + if (strcmp(st, "$(addprefix $(intermediates)/, \\") == 0) + continue; + } else if (st[0] == ')' && st[1] == '\0') + continue; + static const char bindHead[] = "bindings/js/"; + const size_t bindLen = sizeof(bindHead) - 1; + string escaped; + if (strncmp(st, bindHead, bindLen) == 0) { + st += bindLen; + if (MarkDerivedFound(derived, st, strlen(st)) == false) { + fprintf(stderr, "*** webkit removed js binding: %s" + " (must be removed manually)\n", st); + escaped = SedEscape(st); + fprintf(commandFile, "-e '/%s/ d' ", escaped.c_str()); + } + continue; + } + myassert(strncmp(st, "$(intermediates)", 16) == 0); + if (onGen && inGen == false) { // no continuation + char* lastSlash; + myassert(strrchr(st, '/') != NULL); + while ((lastSlash = strrchr(st, '/')) != NULL) { + char* lastEnd = strchr(lastSlash, ' '); + if (lastEnd == NULL) + lastEnd = lastSlash + strlen(lastSlash); + myassert(lastSlash != 0); + lastSlash++; + size_t lastLen = lastEnd - lastSlash; + if (MarkDerivedFound(derived, lastSlash, lastLen) == false) { + fprintf(stderr, "*** webkit removed generated file:" + " %.*s (must be removed manually)\n", (int) lastLen, + lastSlash); + // escaped = SedEscape(st); + // fprintf(commandFile, "-e '/%s/ d' \\\n", escaped.c_str()); + } + char* priorDollar = strrchr(st, '$'); + myassert(priorDollar != NULL); + char* nextDollar = strchr(st, '$'); + if (nextDollar == priorDollar) + break; + priorDollar[0] = '\0'; + } + continue; + } + char* nextSt = nextStart; + skipSpace(&nextSt); + myassert(strncmp(nextSt, "$(intermediates)", 16) != 0 || strcmp(st, nextSt) < 0); + // do { + char* bind = bindings; + myassert(bind); + skipSpace(&bind); + int compare = strncmp(bind, st, strlen(bind) - 2); + if (compare < 0) { // add a file + escaped = SedEscape(st); + char* filename = strrchr(bindings, '/'); + myassert(filename); + filename += 3; + // FIX ME: exclude items in DerivedSources.make all : $(filter-out ... + char* bi = bindings; + skipSpace(&bi); + char* bindName = strrchr(bi, '/'); + myassert(bindName != NULL); + bindName++; + string biStr = SedEscape(bi); + fprintf(commandFile, "-e '/%s/ i\\\n_TAB_%s \\\\\n' ", + escaped.c_str(), biStr.c_str()); + MarkDerivedFound(derived, bindName, strlen(bindName)); + nextStart = start; + bindings += strlen(bindings) + 1; + inGen = true; + } else if (compare > 0) { + // if file to be deleted is locally added by android, leave it alone + myassert(strncmp(st, "$(intermediates)/", 17) == 0); + string subst = string(st).substr(17, strlen(st) - 17 - (inGen ? 2 : 0)); + string localDerivedStr = ScratchFile("LocalDerived"); + string filter = string("echo '") + subst + "' | " + androidFiles + + " > " + localDerivedStr; + if (options.debug) + fprintf(stderr, "LocalDerived.txt : %s\n", filter.c_str()); + err = system(filter.c_str()); + myassert(err == 0 || err == 256); + char* localDerived = GetFile(localDerivedStr); + if (localDerived[0] != '\0') { + escaped = SedEscape(st); + fprintf(commandFile, "-e '/%s/ d' ", escaped.c_str()); + } + } else { + char* stName = strrchr(st, '/'); + myassert(stName); + stName++; + MarkDerivedFound(derived, stName, strlen(stName)); + bindings += strlen(bindings) + 1; + } + // } while (strstr(start, "$(intermediates)") != NULL); + // if changing directories, add any new files to the end of this directory first + if (bindings[0] != '\0' && strstr(nextStart, "$(intermediates)") == NULL) { + st = start; + skipSpace(&st); + escaped = SedEscape(st); + st = strchr(st, '/'); + char* stDirEnd = strchr(st + 1, '/'); + do { + bind = strchr(bindings, '/'); + if (!bind) + break; + char* bindEnd = strchr(bind + 1, '/'); + if (!bindEnd) + break; + if (bindEnd - bind != stDirEnd - st) + break; + if (strncmp(st, bind, stDirEnd - st) != 0) + break; + if (inGen == false) + fprintf(commandFile, "-e '/%s/ s/$/ \\\\/' ", escaped.c_str()); + char* bi = bindings; + skipSpace(&bi); + string biStr = SedEscape(bi); + fprintf(commandFile, "-e '/%s/ a\\\n_TAB_%s\n' ", + escaped.c_str(), biStr.c_str()); + MarkDerivedFound(derived, bindEnd + 1, strlen(bindEnd + 1)); + escaped = biStr; + inGen = false; + bindings += strlen(bindings) + 1; + } while (true); + } + } while (start = nextStart, start < fileEnd); + for (unsigned index = 0; index < derived.size(); index++) { + char* der = derived[index]; + if (der[0] == '\0') + continue; + string excludedGeneratedStr = ScratchFile("ExcludedGenerated"); + string filter = string("echo '") + der + "' | " + excludedGenerated + + " > " + excludedGeneratedStr; + err = system(filter.c_str()); + myassert(err == 0 || err == 256); + char* excluded = GetFile(excludedGeneratedStr); + if (excluded[0] != '\0') + fprintf(stderr, "*** missing rule to generate %s\n", der); + } + fprintf(commandFile, " | sed 's/^_TAB_/\t/' > %s/%s/%s\n", sandboxCmd, dir, "xAndroid.derived.mk"); + fprintf(commandFile, "mv %s/%s/%s %s/%s/%s\n", + sandboxCmd, dir, "xAndroid.derived.mk", sandboxCmd, dir, "Android.derived.mk"); + if (options.emitGitCommands) + fprintf(commandFile, "git add %s/%s\n", dir, "Android.derived.mk"); +} + +int MatchLen(const char* one, const char* two, size_t len) +{ + bool svgIn1 = strstr(one, "svg") || strstr(one, "SVG"); + bool svgIn2 = strstr(two, "svg") || strstr(two, "SVG"); + if (svgIn1 != svgIn2) + return 0; + int signedLen = (int) len; + int original = signedLen; + while (*one++ == *two++ && --signedLen >= 0) + ; + return original - signedLen; +} + +// create the list of sed commands to update the WebCore Make file +void UpdateMake(const char* dir) +{ + // read in the makefile + size_t makeSize; + char* start, * localStart = NULL; + string excludedDirs, excludedFiles, androidFiles; + char* makeFile = GetMakeAndExceptions(dir, "Android.mk", &makeSize, + &excludedFiles, NULL, &excludedDirs, &androidFiles, &start, &localStart); + char* lastFileName = NULL; + size_t lastFileNameLen = 0; + int lastLineNumber = -1; + // get the actual list of files + string updateMakeStr = ScratchFile(__FUNCTION__); + string filelist = string("cd ") + newBase + "/" + dir + " ;" + " find . -name '*.cpp' -or -name '*.c' -or -name '*.y' | " + + excludedFiles + " | sed -e 's/.\\///' " + excludedDirs + + " | sort -o " + updateMakeStr; + if (options.debug) + fprintf(stderr, "make %s/%s filter: %s\n", dir, "Android.mk", filelist.c_str()); + int err = system(filelist.c_str()); + myassert(err == 0 || err == 256); + char* newList = GetFile(updateMakeStr); + do { // find first filename in makefile + if (strncmp(start, "LOCAL_SRC_FILES := \\", 20) == 0) + break; + start += strlen(start) + 1; + } while (start < makeFile + makeSize); + myassert(start[0] != '\0'); + if (options.emitPerforceCommands) + fprintf(commandFile, "p4 edit %s/%s/%s\n", sandboxCmd, dir, "Android.mk"); + fprintf(commandFile, "cat %s/%s/%s | sed ", sandboxCmd, dir, "Android.mk"); + int lineNumber = 0; + do { + start += strlen(start) + 1; + lineNumber++; + if (start - makeFile >= makeSize || start[0] == '$') + break; + if (start[0] == '\0' || !isspace(start[0])) + continue; + skipSpace(&start); + if (start[0] == '\0' || start[0] == '\\') + continue; + size_t startLen = strlen(start); + if (start[startLen - 1] == '\\') + --startLen; + while (isspace(start[startLen - 1])) + --startLen; + size_t newListLen = strlen(newList); + if (lastFileName != NULL) { + myassert(strncmp(start, lastFileName, startLen) > 0 || + startLen > lastFileNameLen); + } + if (strstr(start, "android") != NULL || strstr(start, "Android") != NULL) { + if (startLen == newListLen && strncmp(newList, start, startLen) == 0) + newList += newListLen + 1; + lastFileName = start; + lastFileNameLen = startLen; + lastLineNumber = lineNumber; + continue; + } + int compare; + bool backslash = lastFileName && + lastFileName[strlen(lastFileName) - 1] == '\\'; + do { + compare = strncmp(newList, start, startLen); + if (compare == 0 && startLen != newListLen) + compare = newListLen < startLen ? -1 : 1; + if (newList[0] == '\0' || compare >= 0) + break; + // add a file + if (lastFileName && lineNumber - lastLineNumber > 1 && + MatchLen(lastFileName, newList, lastFileNameLen) > + MatchLen(start, newList, startLen)) { + string escaped = SedEscape(lastFileName); + if (!backslash) + fprintf(commandFile, "-e '/%s/ s/$/ \\\\/' ", escaped.c_str()); + fprintf(commandFile, "-e '/%s/ a\\\n_TAB_%s%s\n' ", + SedEscape(lastFileName).c_str(), newList, + backslash ? " \\\\" : ""); + lastFileName = newList; + lastFileNameLen = newListLen; + } else { + fprintf(commandFile, "-e '/%s/ i\\\n_TAB_%s \\\\\n' ", + SedEscape(start).c_str(), newList); + } + newList += newListLen + 1; + newListLen = strlen(newList); + } while (true); + if (newList[0] == '\0' || compare > 0) { + // don't delete files added by Android + string localMakeStr = ScratchFile("LocalMake"); + string filter = "echo '" + string(start).substr(0, startLen) + + "' | " + androidFiles + " > " + localMakeStr; + int err = system(filter.c_str()); + myassert(err == 0 || err == 256); + char* localMake = GetFile(localMakeStr); + if (localMake[0] != '\0') { + string escaped = SedEscape(start); + fprintf(commandFile, "-e '/%s/ d' ", escaped.c_str()); + } + } else + newList += newListLen + 1; + lastFileName = start; + lastFileNameLen = startLen; + lastLineNumber = lineNumber; + } while (true); + fprintf(commandFile, " | sed 's/^_TAB_/\t/' > %s/%s/%s\n", sandboxCmd, dir, "xAndroid.mk"); + fprintf(commandFile, "mv %s/%s/%s %s/%s/%s\n", + sandboxCmd, dir, "xAndroid.mk", sandboxCmd, dir, "Android.mk"); + if (options.emitGitCommands) + fprintf(commandFile, "git add %s/%s\n", dir, "Android.mk"); +} + +static bool emptyDirectory(const char* base, const char* work, const char* dir) { + string findEmptyStr = "find \""; + string emptyDirStr = ScratchFile("emptyDirectory"); + if (base[0] != '\0') + findEmptyStr += string(base) + "/" + work + "/" + dir + "\""; + else + findEmptyStr += string(work) + "/" + dir + "\""; + findEmptyStr += " -type f -print > " + emptyDirStr; + int err = system(findEmptyStr.c_str()); + if (err != 0) + return true; + FILE* file = fopen(emptyDirStr.c_str(), "r"); + if (file == NULL) + { + fprintf(stderr, "can't read %s\n", emptyDirStr.c_str()); + myassert(0); + return true; + } + fseek(file, 0, SEEK_END); + size_t size = ftell(file); + fclose(file); + return size == 0; +} + +static bool emptyDirectory(const char* work, const char* dir) { + return emptyDirectory("", work, dir); +} + +void CompareDirs(const char* workingDir, bool renamePass) +{ + map renameMap; + char* oldList = List(oldBase, "old", workingDir); + char* newList = List(newBase, "new", workingDir); + char* sandList = List(sandboxBase, "sandbox", workingDir); + char* oldMem = oldList; + char* newMem = newList; + char* sandboxMem = sandList; + // identify files to be added, removed by comparing old, new lists + do { + size_t oldLen = strlen(oldList); + size_t newLen = strlen(newList); + size_t sandLen = strlen(sandList); + if (oldLen == 0 && newLen == 0 && sandLen == 0) + break; + bool oldDir = false; + bool oldExecutable = false; + if (oldLen > 0) { + char last = oldList[oldLen - 1]; + oldDir = last == '/'; + oldExecutable = last == '*'; + if (oldDir || oldExecutable) + --oldLen; + } + bool newDir = false; + bool newExecutable = false; + if (newLen > 0) { + char last = newList[newLen - 1]; + newDir = last == '/'; + newExecutable = last == '*'; + if (newDir || newExecutable) + --newLen; + } + bool sandDir = false; + bool sandExecutable = false; + if (sandLen > 0) { + char last = sandList[sandLen - 1]; + sandDir = last == '/'; + sandExecutable = last == '*'; + if (sandDir || sandExecutable) + --sandLen; + } + string oldFileStr = string(oldList).substr(0, oldLen); + const char* oldFile = oldFileStr.c_str(); + string newFileStr = string(newList).substr(0, newLen); + const char* newFile = newFileStr.c_str(); + string sandFileStr = string(sandList).substr(0, sandLen); + const char* sandFile = sandFileStr.c_str(); + int order = Compare(oldList, oldLen, newList, newLen); + int sandOrder = 0; + if ((oldLen > 0 || sandLen > 0) && order <= 0) { + sandOrder = Compare(sandList, sandLen, oldList, oldLen); + if (sandOrder > 0 && renamePass == false) + fprintf(stderr, "error: file in old webkit missing from sandbox: %s/%s\n", + workingDir, oldFile); + if (sandOrder < 0 && renamePass == false) { + // file added by android -- should always have name 'android?' + const char* android = strstr(sandFile, "ndroid"); + if (android == NULL) + fprintf(stderr, "warning: expect added %s to contain 'android': %s/%s\n", + sandDir ? "directory" : "file" , workingDir, sandFile); + } + if (sandOrder == 0) { + myassert(oldDir == sandDir); + if (oldExecutable != sandExecutable) + CheckForExec(oldBase, workingDir, oldList, + sandboxBase, workingDir, sandList, 0, 0); + } + if (sandOrder <= 0) + sandList += strlen(sandList) + 1; + if (sandOrder < 0) + continue; + } + if (order < 0) { // file in old list is not in new + // check to see if file is read only ; if so, call p4 delete -- otherwise, just call delete + if (oldDir == false) { + bool modifiedFile = false; + // check to see if android modified deleted file + if (sandOrder == 0) { + string rename(workingDir); + rename.append("/"); + rename.append(oldFile); + if (renamePass) { + string newName = Find(oldFile); + if (newName.length() > 0) { + map::iterator iter = renameMap.find(rename); + myassert(iter == renameMap.end()); // if I see the same file twice, must be a bug + renameMap[rename] = newName; + myassert(rename != newName); + if (options.debug) + fprintf(stderr, "map %s to %s\n", rename.c_str(), newName.c_str()); + } + } + if (renamePass == false) { + bool oldSandboxDiff = CompareFiles(oldBase, sandboxBase, workingDir, oldList); + const char* renamedDir = workingDir; + map::iterator iter = renameMap.find(rename); + if (iter != renameMap.end()) { + string newName = renameMap[rename]; + renamedDir = newName.c_str(); + char* renamed = (char*) strrchr(renamedDir, '/'); + *renamed++ = '\0'; // splits rename into two strings + if (options.emitPerforceCommands) { + fprintf(commandFile, "p4 integrate \"%s/%s/%s\" \"%s/%s/%s\"\n", sandboxCmd, workingDir, oldFile, + sandboxCmd, renamedDir, renamed); + fprintf(commandFile, "p4 resolve \"%s/%s/%s\"\n", sandboxCmd, renamedDir, renamed); + } else if (options.emitGitCommands) { + fprintf(commandFile, "git mv \"%s/%s\" \"%s/%s\"\n", workingDir, oldFile, + renamedDir, renamed); + } + if (oldSandboxDiff) { + if (options.emitPerforceCommands) + fprintf(commandFile, "p4 open \"%s/%s/%s\"\n", sandboxCmd, renamedDir, renamed); + fprintf(commandFile, "merge -q \"%s/%s/%s\" \"%s/%s/%s\" \"%s/%s/%s\"\n", + sandboxCmd, renamedDir, renamed, oldCmd, workingDir, oldFile, newCmd, renamedDir, renamed); + bool success = Merge(workingDir, oldFile, renamedDir, renamed, "/dev/null"); + if (success == false) { + fprintf(stderr, "*** Manual merge required: %s/%s\n", renamedDir, renamed); + fprintf(commandFile, "cat \"%s/%s/%s\" | sed -e 's/^<<<<<<<.*$/#ifdef MANUAL_MERGE_REQUIRED/' " + "-e 's/^=======$/#else \\/\\/ MANUAL_MERGE_REQUIRED/' " + "-e 's/^>>>>>>>.*$/#endif \\/\\/ MANUAL_MERGE_REQUIRED/' > \"%s/%s/x%s\"\n", + sandboxCmd, renamedDir, renamed, sandboxCmd, renamedDir, renamed); + fprintf(commandFile, "mv \"%s/%s/x%s\" \"%s/%s/%s\"\n", + sandboxCmd, renamedDir, renamed, sandboxCmd, renamedDir, renamed); + } + if (options.emitGitCommands) + fprintf(commandFile, "git add \"%s/%s\"\n", renamedDir, renamed); + } else { + bool oldNewDiff = CompareFiles(oldBase, workingDir, oldList, newBase, renamedDir, renamed); + if (oldNewDiff) { + if (options.emitPerforceCommands) + fprintf(oopsFile, "p4 open \"%s/%s/%s\"\n", sandboxCmd, renamedDir, renamed); + fprintf(oopsFile, "cp \"%s/%s/%s\" \"%s/%s/%s\"\n", + newCmd, renamedDir, renamed, sandboxCmd, renamedDir, renamed); + if (options.emitGitCommands) + fprintf(oopsFile, "git add \"%s/%s\"\n", renamedDir, renamed); + } + } + } else if (oldSandboxDiff) { + modifiedFile = true; + fprintf(stderr, "*** Modified file deleted: %s/%s\n", workingDir, oldFile); +// FindDeletedAndroidChanges(workingDir, oldFile); + } + } // if renamePass == false + } // if sandOrder == 0 + if (modifiedFile) { + fprintf(commandFile, "cat \"%s/%s/%s\" | sed -e '1 i\\\n#ifdef MANUAL_MERGE_REQUIRED\n' " + "-e '$ a\\\n#endif \\/\\/ MANUAL_MERGE_REQUIRED\n' > \"%s/%s/x%s\"\n", + sandboxCmd, workingDir, oldFile, sandboxCmd, workingDir, oldFile); + fprintf(commandFile, "mv \"%s/%s/x%s\" \"%s/%s/%s\"\n", + sandboxCmd, workingDir, oldFile, sandboxCmd, workingDir, oldFile); + } else if (options.emitPerforceCommands) + fprintf(commandFile, "p4 delete \"%s/%s/%s\"\n", sandboxCmd, workingDir, oldFile); + else if (options.emitGitCommands) + fprintf(commandFile, "git rm \"%s/%s\"\n", workingDir, oldFile); + else + fprintf(commandFile, "rm \"%s/%s/%s\"\n", sandboxCmd, workingDir, oldFile); + } else { // if oldDir != false + // !!! FIXME start here; + // check to see if old directory is empty ... (e.g., WebCore/doc) + // ... and/or isn't in sandbox anyway (e.g., WebCore/LayoutTests) + // if old directory is in sandbox (e.g. WebCore/kcanvas) that should work + if (options.emitPerforceCommands) + fprintf(commandFile, "p4 delete \"%s/%s/%s/...\"\n", sandboxCmd, workingDir, oldFile); + else if (options.emitGitCommands) + fprintf(commandFile, "git rm \"%s/%s/...\"\n", workingDir, oldFile); + else + fprintf(commandFile, "rm \"%s/%s/%s/...\"\n", sandboxCmd, workingDir, oldFile); + if (renamePass == false) + fprintf(stderr, "*** Directory deleted: %s/%s\n", workingDir, oldFile); + } + oldList += strlen(oldList) + 1; + continue; + } + if (order > 0) { + if (renamePass == false) { + string rename(workingDir); + rename.append("/"); + rename.append(newFile); + bool skip = false; + for (map::iterator iter = renameMap.begin(); iter != renameMap.end(); iter++) { + if (iter->second == rename) { + skip = true; + break; + } + } + if (skip == false) { + if (newDir) { + if (strcmp(sandFile, newFile) != 0 && + emptyDirectory(newBase, workingDir, newFile) == false) { + fprintf(copyDirFile, "find \"%s/%s/%s\" -type d -print | " + "sed 's@%s/\\(.*\\)@mkdir %s/\\1@' | bash -s\n", + newCmd, workingDir, newFile, newCmd, sandboxCmd); + fprintf(copyDirFile, "find \"%s/%s/%s\" -type f -print | " + "sed 's@%s/\\(.*\\)@cp %s/\\1 %s/\\1@' | bash -s\n", + newCmd, workingDir, newFile, newCmd, newCmd, sandboxCmd); + if (options.emitPerforceCommands) + fprintf(copyDirFile, "find \"%s/%s/%s\" -type f -print | " + "p4 -x - add\n", + sandboxCmd, workingDir, newFile); + else if (options.emitGitCommands) + fprintf(copyDirFile, "git add \"%s/%s\"\n", + workingDir, newFile); + } + } else { +// if (emptyDirectory(sandboxBase, workingDir)) { +// fprintf(commandFile, "mkdir \"%s/%s\"\n", sandboxCmd, workingDir); +// } + bool edit = false; + size_t newLen1 = strlen(newFile); + for (map::iterator iter = renameMap.begin(); iter != renameMap.end(); iter++) { + if (strcmp(iter->second.c_str(), workingDir) == 0) { + const char* first = iter->first.c_str(); + size_t firstLen = strlen(first); + if (firstLen > newLen1 && strcmp(newFile, + &first[firstLen - newLen1]) == 0) { + edit = true; + break; + } + } + } + if (edit == false) { + fprintf(commandFile, "cp \"%s/%s/%s\" \"%s/%s/%s\"\n", + newCmd, workingDir, newFile, sandboxCmd, workingDir, newFile); + if (options.emitPerforceCommands) + fprintf(commandFile, "p4 add \"%s/%s/%s\"\n", sandboxCmd, workingDir, newFile); + else if (options.emitGitCommands) + fprintf(commandFile, "git add \"%s/%s\"\n", workingDir, newFile); + } + } + } + } + newList += strlen(newList) + 1; + continue; + } + if (oldDir) { + myassert(newDir); + size_t newLen1 = strlen(workingDir) + strlen(oldList); + char* newFile = new char[newLen1 + 1]; + sprintf(newFile, "%s/%.*s", workingDir, (int) strlen(oldList) - 1, + oldList); + if (sandOrder > 0) { // file is on old and new but not sandbox + if (emptyDirectory(newBase, newFile) == false) { + fprintf(copyDirFile, "find \"%s/%s\" -type d -print | " + "sed 's@%s/\\(.*\\)@mkdir %s/\\1@' | bash -s\n", + newCmd, newFile, newCmd, sandboxCmd); + fprintf(copyDirFile, "find \"%s/%s\" -type f -print | " + "sed 's@%s/\\(.*\\)@cp %s/\\1 %s/\\1@' | bash -s\n", + newCmd, newFile, newCmd, newCmd, sandboxCmd); + if (options.emitPerforceCommands) + fprintf(copyDirFile, "find \"%s/%s\" -type f -print | " + "p4 -x - add\n", + sandboxCmd, newFile); + else if (options.emitGitCommands) + fprintf(copyDirFile, "git add \"%s\"", newFile); + } + } else + CompareDirs(newFile, renamePass); + delete[] newFile; + } else { + // at this point, the file is in both old and new webkits; see if it changed + // ignore executables, different or not (or always copy, or do text compare? or find binary compare? ) + if (oldExecutable != newExecutable) + fprintf(stderr, "*** %s/%s differs in the execute bit (may cause problems for perforce)\n", workingDir, oldFile); + // myassert(sandOrder != 0 || sandFile[sandLen - 1] == '*'); + // Diff(oldBase, sandboxBase, workingDir, oldFile); + bool oldNewDiff = CompareFiles(oldBase, newBase, workingDir, oldList); + if (oldNewDiff && sandOrder == 0 && renamePass == false) { // if it changed, see if android also changed it + if (options.emitPerforceCommands) + fprintf(commandFile, "p4 edit \"%s/%s/%s\"\n", sandboxCmd, workingDir, oldFile); + bool oldSandboxDiff = CompareFiles(oldBase, sandboxBase, workingDir, oldFile); + if (oldSandboxDiff) { + fprintf(commandFile, "merge -q \"%s/%s/%s\" \"%s/%s/%s\" \"%s/%s/%s\"\n", + sandboxCmd, workingDir, oldFile, oldCmd, workingDir, oldFile, newCmd, workingDir, oldFile); + bool success = Merge(workingDir, oldFile); + if (success == false) { + fprintf(stderr, "*** Manual merge required: %s/%s\n", workingDir, oldFile); + fprintf(commandFile, "cat \"%s/%s/%s\" | sed -e 's/^<<<<<<<.*$/#ifdef MANUAL_MERGE_REQUIRED/' " + "-e 's/^=======$/#else \\/\\/ MANUAL_MERGE_REQUIRED/' -e 's/^>>>>>>>.*$/#endif \\/\\/ MANUAL_MERGE_REQUIRED/' > \"%s/%s/x%s\"\n", + sandboxCmd, workingDir, oldFile, sandboxCmd, workingDir, oldFile); + fprintf(commandFile, "mv \"%s/%s/x%s\" \"%s/%s/%s\"\n", + sandboxCmd, workingDir, oldFile, sandboxCmd, workingDir, oldFile); + } + } else fprintf(commandFile, "cp \"%s/%s/%s\" \"%s/%s/%s\"\n", newCmd, workingDir, oldFile , + sandboxCmd, workingDir, oldFile); + if (options.emitGitCommands) + fprintf(commandFile, "git add \"%s/%s\"\n", workingDir, oldFile); + } + } + myassert(oldLen == newLen); + newList += strlen(newList) + 1; + oldList += strlen(oldList) + 1; + } while (true); + delete[] oldMem; + delete[] newMem; + delete[] sandboxMem; +} + +bool Ignore(char* fileName, size_t len) +{ + if (len == 0) + return true; + if (fileName[len - 1] =='/') + return true; + if (strcmp(fileName, ".DS_Store") == 0) + return true; + if (strcmp(fileName, ".ignoreSVN") == 0) + return true; + return false; +} + +void FixStar(char* fileName, size_t len) +{ + if (fileName[len - 1] =='*') + fileName[len - 1] = '\0'; +} + +void FixColon(char** fileNamePtr, size_t* lenPtr) +{ + char* fileName = *fileNamePtr; + size_t len = *lenPtr; + if (fileName[len - 1] !=':') + return; + fileName[len - 1] = '\0'; + if (strncmp(fileName ,"./", 2) != 0) + return; + fileName += 2; + *fileNamePtr = fileName; + len -= 2; + *lenPtr = len; +} + +bool IgnoreDirectory(const char* dir, const char** dirList) +{ + if (dirList == NULL) + return false; + const char* test; + while ((test = *dirList++) != NULL) { + if (strncmp(dir, test, strlen(test)) == 0) + return true; + } + return false; +} + +static void doSystem(char* scratch) +{ + if (false) printf("%s\n", scratch); + int err = system(scratch); + myassert(err == 0); +} + +static void copyToCommand(char* scratch, string file) +{ + doSystem(scratch); + char* diff = GetFile(file.c_str()); + while (*diff) { + fprintf(commandFile, "%s\n", diff); + diff += strlen(diff) + 1; + } +} + +#define WEBKIT_EXCLUDED_DIRECTORIES \ + "-not -path \"*Tests\" " /* includes LayoutTests, PageLoadTests */ \ + "-not -path \"*Tests/*\" " /* includes LayoutTests, PageLoadTests */ \ + "-not -path \"*Site\" " /* includes BugsSite, WebKitSite */ \ + "-not -path \"*Site/*\" " /* includes BugsSite, WebKitSite */ \ + "-not -path \"./PlanetWebKit/*\" " \ + "-not -path \"./PlanetWebKit\" " + +#define ANDROID_EXCLUDED_FILES \ + "-e '/^Files .* differ/ d' " \ + "-e '/^Only in .*/ d' " \ + "-e '/Android.mk/ d' " \ + "-e '/android$/ d' " + +#define ANDROID_EXCLUDED_DIRS \ + "-e '/\\/JavaScriptCore\\// d' " \ + "-e '/\\/WebCore\\// d' " + +#define ANDROID_EXCLUDED_DIRS_GIT \ + "-e '/ JavaScriptCore\\// d' " \ + "-e '/ WebCore\\// d' " + +void CopyOther() +{ + string excludedFiles = ANDROID_EXCLUDED_FILES; + if (options.emitGitCommands) + excludedFiles += ANDROID_EXCLUDED_DIRS_GIT; + else + excludedFiles += ANDROID_EXCLUDED_DIRS; + char scratch[1024]; + // directories to ignore in webkit + string copyOtherWebKit = ScratchFile("CopyOtherWebKit"); + sprintf(scratch, "cd %s ; find . -type d -not -empty " + WEBKIT_EXCLUDED_DIRECTORIES + " > %s", newBase, copyOtherWebKit.c_str()); + doSystem(scratch); + // directories to ignore in android + string copyOtherAndroid = ScratchFile("CopyOtherAndroid"); + sprintf(scratch, "cd %s ; find . -type d -not -empty " + "-not -path \"*.git*\" " + "-not -path \"*android*\" " + " > %s", sandboxBase, copyOtherAndroid.c_str()); + doSystem(scratch); + if (0) { + string copyOtherMkDir = ScratchFile("CopyOtherMkDir"); + sprintf(scratch, "diff %s %s | sed -e 's@< \\./\\(.*\\)$" + "@mkdir %s/\\1@' " + "-e '/^[0-9].*/ d' " + "-e '/>.*/ d' " + "-e '/---/ d' " + "-e '/\\/JavaScriptCore\\// d' " + "-e '/\\/WebCore\\// d' " + "> %s", copyOtherWebKit.c_str(), copyOtherAndroid.c_str(), sandboxCmd, + copyOtherMkDir.c_str()); + if (options.debug) + fprintf(stderr, "%s\n", scratch); + copyToCommand(scratch, copyOtherMkDir); + } + string copyOtherDiff = ScratchFile("CopyOtherDiff"); + int scratchLen = sprintf(scratch, "diff %s %s | sed -e 's@< \\./\\(.*\\)$" + "@mkdir -p -v %s/\\1 ; find %s/\\1 -type f -depth 1 -exec cp {} %s/\\1 \";\"", + copyOtherWebKit.c_str(), copyOtherAndroid.c_str(), sandboxCmd, newCmd, + sandboxCmd); + if (options.emitGitCommands) + scratchLen += sprintf(&scratch[scratchLen], " ; cd %s ; find ", sandboxCmd); + else + scratchLen += sprintf(&scratch[scratchLen], " ; find %s/", sandboxCmd); + scratchLen += sprintf(&scratch[scratchLen], "\\1 -type f -depth 1 | "); + if (options.emitPerforceCommands) + scratchLen += sprintf(&scratch[scratchLen], "p4 -x - add "); + else if (options.emitGitCommands) + scratchLen += sprintf(&scratch[scratchLen], "xargs git add "); + scratchLen += sprintf(&scratch[scratchLen], + "@' -e '/^[0-9].*/ d' " + "-e '/>.*/ d' " + "-e '/---/ d' " + "-e '/\\/JavaScriptCore\\// d' " + "-e '/\\/WebCore\\// d' " + "> %s", copyOtherDiff.c_str()); + if (options.debug) + fprintf(stderr, "%s\n", scratch); + copyToCommand(scratch, copyOtherDiff); + string deleteOtherDiff = ScratchFile("DeleteOtherDiff"); + scratchLen = sprintf(scratch, "diff -r -q %s %s | sed -e " + "'s@Only in %s/\\(.*\\)\\: \\(.*\\)@", + newBase, sandboxBase, sandboxBase); + if (options.emitPerforceCommands) + scratchLen += sprintf(&scratch[scratchLen], "p4 delete %s/", sandboxCmd); + else if (options.emitGitCommands) + scratchLen += sprintf(&scratch[scratchLen], "git rm "); + else + scratchLen += sprintf(&scratch[scratchLen], "rm %s/", sandboxCmd); + scratchLen += sprintf(&scratch[scratchLen], "\\1/\\2@' %s > %s", + excludedFiles.c_str(), deleteOtherDiff.c_str()); + if (options.debug) + fprintf(stderr, "%s\n", scratch); + copyToCommand(scratch, deleteOtherDiff); + string addOtherDiff = ScratchFile("AddOtherDiff"); + scratchLen = sprintf(scratch, "diff -r -q %s %s | sed -e " + "'s@Only in %s/\\(.*\\)\\: \\(.*\\)" + "@mkdir -p -v %s/\\1 ; cp %s/\\1/\\2 %s/\\1/\\2 ; ", + newBase, sandboxBase, newBase, sandboxCmd, newCmd, sandboxCmd); + if (options.emitPerforceCommands) + scratchLen += sprintf(&scratch[scratchLen], + "p4 add %s/\\1/\\2", sandboxCmd); + else if (options.emitGitCommands) + scratchLen += sprintf(&scratch[scratchLen], + "git add \\1/\\2"); + scratchLen += sprintf(&scratch[scratchLen], "@' %s > %s", + excludedFiles.c_str(), addOtherDiff.c_str()); + if (options.debug) + fprintf(stderr, "%s\n", scratch); + copyToCommand(scratch, addOtherDiff); + string editOtherDiff = ScratchFile("EditOtherDiff"); + scratchLen = sprintf(scratch, "diff -r -q %s %s | sed -e " + "'s@Files %s/\\(.*\\) and %s/\\(.*\\) differ@", + newBase, sandboxBase, newBase, sandboxBase); + if (options.emitPerforceCommands) + scratchLen += sprintf(&scratch[scratchLen], + "p4 edit %s/\\2 ; ", sandboxCmd); + scratchLen += sprintf(&scratch[scratchLen], "cp %s/\\1 %s/\\2 ", + newCmd, sandboxCmd); + if (options.emitGitCommands) + scratchLen += sprintf(&scratch[scratchLen], + " ; git add \\2"); + scratchLen += sprintf(&scratch[scratchLen], "@' %s > %s", + excludedFiles.c_str(), editOtherDiff.c_str()); + if (options.debug) + fprintf(stderr, "%s\n", scratch); + copyToCommand(scratch, editOtherDiff); +} + +void MakeExecutable(const string& filename) +{ + string makeExScript = "chmod +x "; + makeExScript += filename; + int err = system(makeExScript.c_str()); + myassert(err == 0); +} + +bool ReadArgs(char* const args[], int argCount) +{ + int index = 0; + const char* toolpath = args[index++]; + // first arg is path to this executable + // use this to build default paths + + for (; index < argCount; index++) { + const char* arg = args[index]; + if (strncmp(arg, "-a", 2) == 0 || strcmp(arg, "--android") == 0) { + index++; + options.androidWebKit = args[index]; + continue; + } + if (strncmp(arg, "-b", 2) == 0 || strcmp(arg, "--basewebkit") == 0) { + index++; + options.baseWebKit = args[index]; + continue; + } + if (strncmp(arg, "-c", 2) == 0 || strcmp(arg, "--mergecore") == 0) { + options.clearOnce(); + options.mergeCore = true; + continue; + } + if (strncmp(arg, "-d", 2) == 0 || strcmp(arg, "--debug") == 0) { + options.debug = true; + continue; + } + if (strncmp(arg, "-e", 2) == 0 || strcmp(arg, "--emptydirs") == 0) { + options.clearOnce(); + options.removeEmptyDirs = true; + continue; + } + if (strncmp(arg, "-g", 2) == 0 || strcmp(arg, "--git") == 0) { + options.emitGitCommands = true; + if (options.emitPerforceCommands == false) + continue; + } + if (strncmp(arg, "-m", 2) == 0 || strcmp(arg, "--mergemake") == 0) { + options.clearOnce(); + options.mergeMake = true; + continue; + } + if (strncmp(arg, "-n", 2) == 0 || strcmp(arg, "--newwebkit") == 0) { + index++; + options.newWebKit = args[index]; + continue; + } + if (strncmp(arg, "-o", 2) == 0 || strcmp(arg, "--copyother") == 0) { + options.clearOnce(); + options.copyOther = true; + continue; + } + if (strncmp(arg, "-p", 2) == 0 || strcmp(arg, "--perforce") == 0) { + options.emitPerforceCommands = true; + if (options.emitGitCommands == false) + continue; + } + if (strncmp(arg, "-s", 2) == 0 || strcmp(arg, "--removesvn") == 0) { + options.clearOnce(); + options.removeSVNDirs = true; + continue; + } + if (strncmp(arg, "-v", 2) == 0 || strcmp(arg, "--verbose") == 0) { + options.verbose = true; + fprintf(stderr, "path: %s\n", toolpath); + int err = system("pwd > pwd.txt"); + myassert(err != -1); + fprintf(stderr, "pwd: %s\n", GetFile("pwd.txt")); + system("rm pwd.txt"); + continue; + } + if (strncmp(arg, "-x", 2) == 0 || strcmp(arg, "--execute") == 0) { + options.execute = true; + continue; + } + if (options.emitGitCommands && options.emitPerforceCommands) + printf("choose one of --git and --perforce\n"); + else if (strncmp(arg, "-h", 2) != 0 && strcmp(arg, "--help") != 0 && strcmp(arg, "-?") != 0) + printf("%s not understood\n", args[index]); + printf( +"WebKit Merge for Android version 1.1\n" +"Usage: webkitmerge -a path -b path -n path [-g or -p] [-c -d -e -m -o -s -v -x]\n" +"Options -c -e -m -o -s are set unless one or more are passed.\n" +"Leave -g and -p unset to copy, merge, and delete files outside of source control.\n" +"-a --android path Set the Android webkit path to merge to.\n" +"-b --basewebkit path Set the common base for Android and the newer webkit.\n" +"-c --mergecore Create merge scripts for WebCore, JavaScriptCore .h .cpp.\n" +"-d --debug Show debugging printfs; loop forever on internal assert.\n" +"-e --emptydirs Remove empty directories from webkit trees.\n" +"-g --git Emit git commands.\n" +"-h --help Show this help.\n" +"-m --mergemake Create merge scripts for WebCore/Android.mk,\n" +" WebCore/Android.derived.mk, and JavaScriptCore/Android.mk.\n" +"-n --newwebkit path Set the webkit to merge from.\n" +"-o --copyother Create script to copy other webkit directories.\n" +"-p --perforce Emit perforce commands.\n" +"-s --removesvn Remove svn directories from webkit trees.\n" +"-v --verbose Show status printfs.\n" +"-x --execute Execute the merge scripts.\n" + ); + return false; + } + return options.finish(); +} + +int main (int argCount, char* const args[]) +{ + if (ReadArgs(args, argCount) == false) + return 0; + int err; + // First remove all .svn directories + if (options.removeSVNDirs) { + if (options.verbose) + fprintf(stderr, "removing svn directories from %s\n", newBase); + string removeSVNStr = string("find ") + newBase + + " -type d -name \".svn\" -print0 | xargs -0 rm -rf"; + err = system(removeSVNStr.c_str()); + myassert(err == 0); + } + // Remove all empty directories + if (options.removeEmptyDirs) { + if (options.verbose) + fprintf(stderr, "removing empty directories from %s, %s\n", + oldBase, newBase); + string removeEmpty = string("find ") + oldBase + " " + newBase + + " -type d -empty -delete"; + err = system(removeEmpty.c_str()); + myassert(err == 0); + } + if (options.mergeCore /* || options.mergeMake */) { + if (options.verbose) + fprintf(stderr, "building rename map\n"); + commandFile = fopen("/dev/null", "w"); + copyDirFile = fopen("/dev/null", "w"); + CompareDirs("WebCore", true); // build rename map + CompareDirs("JavaScriptCore", true); + fclose(copyDirFile); + fclose(commandFile); + } + if (options.mergeMake) { + if (options.verbose) + fprintf(stderr, "building make.sh\n"); + string makeShell = outputDir + "make.sh"; + commandFile = fopen(makeShell.c_str(), "w"); + if (options.emitGitCommands || options.emitPerforceCommands) + fprintf(commandFile, "cd %s\n", sandboxCmd); + UpdateMake("WebCore"); + UpdateMake("JavaScriptCore"); + UpdateDerivedMake(); + fclose(commandFile); + MakeExecutable(makeShell); + } + if (options.copyOther) { + if (options.verbose) + fprintf(stderr, "building copyOther.sh\n"); + string copyOtherShell = outputDir + "copyOther.sh"; + commandFile = fopen(copyOtherShell.c_str(), "w"); + if (options.emitGitCommands || options.emitPerforceCommands) + fprintf(commandFile, "cd %s\n", sandboxCmd); + CopyOther(); + fclose(commandFile); + MakeExecutable(copyOtherShell); + } + if (options.mergeCore) { + if (options.verbose) + fprintf(stderr, "building command.sh copyDir.sh oops.sh\n"); + string commandShell = outputDir + "command.sh"; + commandFile = fopen(commandShell.c_str(), "w"); + if (options.emitGitCommands || options.emitPerforceCommands) + fprintf(commandFile, "cd %s\n", sandboxCmd); + string copyDirShell = outputDir + "copyDir.sh"; + copyDirFile = fopen(copyDirShell.c_str(), "w"); + if (options.emitGitCommands || options.emitPerforceCommands) + fprintf(copyDirFile, "cd %s\n", sandboxCmd); + string oopsShell = outputDir + "oops.sh"; + oopsFile = fopen(oopsShell.c_str(), "w"); + if (options.emitGitCommands || options.emitPerforceCommands) + fprintf(oopsFile, "cd %s\n", sandboxCmd); + CompareDirs("WebCore", false); // generate command script + CompareDirs("JavaScriptCore", false); + fclose(oopsFile); + fclose(copyDirFile); + fclose(commandFile); + MakeExecutable(oopsShell); + MakeExecutable(copyDirShell); + MakeExecutable(commandShell); + } + if (options.execute) { + if (options.mergeCore) { + if (options.verbose) + fprintf(stderr, "executing command.sh\n"); + string execCommand = "cd " + options.androidWebKit + "; . " + outputDir + "command.sh"; + err = system(execCommand.c_str()); + myassert(err == 0); + if (options.verbose) + fprintf(stderr, "executing copyDir.sh\n"); + string execCopy = "cd " + options.androidWebKit + "; . " + outputDir + "copyDir.sh"; + err = system(execCopy.c_str()); + myassert(err == 0); + } + if (options.mergeMake) { + if (options.verbose) + fprintf(stderr, "executing make.sh\n"); + string execMake = "cd " + options.androidWebKit + "; . " + outputDir + "make.sh"; + err = system(execMake.c_str()); + myassert(err == 0); + } + if (options.copyOther) { + if (options.verbose) + fprintf(stderr, "executing copyOther.sh\n"); + string execCopyOther = "cd " + options.androidWebKit + "; . " + outputDir + "copyOther.sh"; + err = system(execCopyOther.c_str()); + myassert(err == 0); + } + } + if (options.verbose) + fprintf(stderr, "done!\n"); + else { + string rmAllCmd = "rm " + scratchDir + "* ; rmdir " + scratchDir; + err = system(rmAllCmd.c_str()); + myassert(err == 0); + } + return 0; +} + +/* things to do: + when inserting MANUAL_MERGE_REQUIRED, if contents is #preprocessor, balance first? +*/ -- cgit v1.1