(* * * The contents of this file are subject to the Netscape Public License * Version 1.0 (the "NPL"); you may not use this file except in * compliance with the NPL. You may obtain a copy of the NPL at * http://www.mozilla.org/NPL/ * * Software distributed under the NPL is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL * for the specific language governing rights and limitations under the * NPL. * * The Initial Developer of this code under the NPL is Netscape * Communications Corporation. Portions created by Netscape are * Copyright (C) 1998 Netscape Communications Corporation. All Rights * Reserved. *) (* MANIFESTOLib - Reconciles a CW Project file with an external table of contents file. Uses merge sort, one pass per target. by Patrick C. Beard *) (* Global Configuration Properties *) property pShowReport : true -- property pSourceTree : "Homeward:Work:Raptor:src:" property pSourceTree : "Morbeus:Projects:Raptor:src:" on swapDelimiters(newDelimiters) set oldDelimiters to get AppleScript's text item delimiters set AppleScript's text item delimiters to newDelimiters return oldDelimiters end swapDelimiters on setDelimiters(newDelimiters) set AppleScript's text item delimiters to newDelimiters end setDelimiters -- replaces oldChar with newChar in a string. on replace(aString, oldChar, newChar) set newString to "" repeat with aChar in (every character of aString) if (contents of aChar = oldChar) then set newString to newString & newChar else set newString to newString & aChar end if end repeat return newString end replace (* uses "sort" scripting addition to sort a list of strings. *) on sortList(aList) if (aList ­ {}) then return sort aList else return {} end if end sortList -- reads .toc file into a list. on readManifestFile(sourceTree, manifestFile) set oldDelimiters to swapDelimiters(return) set fileRef to false set fileLines to {} try set fileRef to (open for access manifestFile without write permission) -- read entire file into memory, use text items to delimit lines. set fileContents to (read fileRef) repeat with lineRef in (every text item of fileContents) -- ignore lines that start with "#" or are empty. set fileLine to (contents of lineRef) if (fileLine ­ "") and not (fileLine starts with "#") then set fileLines to fileLines & (sourceTree & replace(fileLine, "/", ":")) end if end repeat on error -- ignore errors. end try if (fileRef is not false) then close access fileRef setDelimiters(oldDelimiters) return sortList(fileLines) end readManifestFile (* both of the following depend on ":" as the delimiter character. *) on folderFromPath(filePath) return ((filePath's text items 1 thru ((count of filePath's text items) - 1)) as string) & ":" end folderFromPath on fileFromPath(filePath) return last text item of filePath end fileFromPath (* CW Pro IDE Interface Handlers. *) on openProject(aProjectFile) tell application "CodeWarrior IDE 3.3" -- activate open aProjectFile end tell end openProject (* forces the named project file to be the front window. *) on selectProject(aProjectFile) set projectName to fileFromPath(aProjectFile as text) tell application "CodeWarrior IDE 3.3" if (name of window 1 is not projectName) then select window projectName end if end tell end selectProject on closeProject(aProjectFile) tell application "CodeWarrior IDE 3.3" Close Project end tell end closeProject on getTargets() set targetList to {} set nameList to {} tell application "CodeWarrior IDE 3.3" set currentProject to project document 1 repeat with targetIndex from 1 to (count of targets of currentProject) set currentTarget to (target targetIndex of currentProject) set targetList to targetList & {currentTarget} set nameList to nameList & {name of currentTarget} end repeat return {target:targetList, names:nameList} end tell end getTargets (* uses "info for" scripting addition, to return the file type of a path. *) on getFileType(aFilePath) return file type of (info for alias aFilePath) end getFileType (* returns all "TEXT" files of the named target. *) on getTargetFiles(targetKey) set targetFiles to {} tell application "CodeWarrior IDE 3.3" set currentProject to project document 1 set currentTarget to (target targetKey of currentProject) try -- workaround for CW IDE 3.X bug, loop until error encountered. set fileIndex to 1 repeat until false set targetFile to (target file fileIndex of currentTarget) -- only consider text files, since other platforms won't be managing binaries. -- also, only consider if target file is directly linked. if (linked of targetFile) then set targetFilePath to (Access Paths of targetFile) tell me if (getFileType(targetFilePath) = "TEXT") then set targetFiles to targetFiles & {targetFilePath} end if end tell end if set fileIndex to (fileIndex + 1) end repeat on error msg -- display dialog msg & " file count = " & fileIndex end try end tell return sortList(targetFiles) end getTargetFiles on addTargetFile(targetFile, targetList) tell application "CodeWarrior IDE 3.3" add (project document 1) new target file with data {targetFile} to targets targetList end tell end addTargetFile global gCurrentTarget on setCurrentTarget(currentTargetName) if (gCurrentTarget ­ currentTargetName) then set gCurrentTarget to currentTargetName tell application "CodeWarrior IDE 3.3" Set Current Target currentTargetName end tell end if end setCurrentTarget on removeTargetFile(targetFile) tell application "CodeWarrior IDE 3.3" Remove Files {targetFile} end tell end removeTargetFile on quote(aString) return "'" & aString & "'" end quote on listContains(aList, anItem) repeat with listItem in aList if (contents of listItem = anItem) then return true end if end repeat return false end listContains on showList(aList) choose from list aList with prompt "List:" with empty selection allowed end showList global gProjectModified on ModifyReadOnly(aProjectFile) if (not gProjectModified) then set gProjectModified to true -- so CodeWarrior will notice, must close the file before MROing it. closeProject(aProjectFile) mro aProjectFile openProject(aProjectFile) selectProject(aProjectFile) end if end ModifyReadOnly on makeStream(itemList) return {streamList:itemList, streamCount:count itemList, streamIndex:0} end makeStream (* true is used as the end of stream value. *) property pEOS : true on advanceStream(stream) set itemCount to (streamCount of stream) set itemIndex to (streamIndex of stream) if (itemIndex < itemCount) then set itemIndex to (itemIndex + 1) set (streamIndex of stream) to itemIndex return (item itemIndex of streamList of stream) else return pEOS end if end advanceStream -- returns true if str2 is INFINITELY great, or str1 is less than str2. on precedes(str1, str2) return (str2 = pEOS) or ((str1 ­ pEOS) and (str1 < str2)) end precedes on get_current_application() return last text item of ((path to current application) as text) end get_current_application on get_frontmost_application() return last text item of ((path to frontmost application) as text) end get_frontmost_application on activate_application(applicationName) tell application "Finder" set applicationProcess to (application process applicationName) set frontmost of applicationProcess to true end tell end activate_application on ReconcileProject(sourceTree, projectPath, manifestoPath) -- so we can easily strip off file names from paths. set oldDelimiters to swapDelimiters(":") -- initialize globals. set gCurrentTarget to "" set gProjectModified to false -- convert paths to aliases. set projectFile to alias projectPath set manifestFile to alias manifestoPath -- read the MANIFESTO file into a list of paths. set manifestContents to readManifestFile(sourceTree, manifestFile) -- return manifestContents -- now, start processing the file items, ensuring that the project contains all items. openProject(projectFile) selectProject(projectFile) set targetsList to getTargets() set targetNames to names of targetsList if (pShowReport) then set theReport to "" set addedFiles to "" set removedFiles to "" end if -- push current application to front for speed. -- set frontmostApplication to get_frontmost_application() -- set currentApplication to get_current_application() -- activate_application(currentApplication) -- reconcile all targets with the MANIFEST file. -- this loop should be recoded in PERL for speed. -- IDEA: with sorted lists, can scan both lists, like a merge sort, and make one pass per target. repeat with targetNameRef in targetNames -- switch targets because getTargetFiles now checks to see if file is linked in current target. set targetName to (contents of targetNameRef) set targetFiles to getTargetFiles(targetName) -- hopefully, this list test is fast. if (targetFiles ­ manifestContents) then -- return {count targetFiles, count manifestContents, targetFiles, manifestContents} -- make sure the project file is modifiable. ModifyReadOnly(projectFile) setCurrentTarget(targetName) set targetStream to makeStream(targetFiles) set targetItem to advanceStream(targetStream) set manifestStream to makeStream(manifestContents) set manifestItem to advanceStream(manifestStream) repeat until (manifestItem is pEOS) and (targetItem is pEOS) -- display dialog "m: " & manifestItem & ", t: " & targetFileItem if (manifestItem = targetItem) then -- items match, advance both. set manifestItem to advanceStream(manifestStream) set targetItem to advanceStream(targetStream) else -- return {manifestItem, targetItem} if (precedes(manifestItem, targetItem)) then -- we have an item in manifest, not in project, so we have to add it to the targets. -- display dialog "adding " & manifestItem addTargetFile(manifestItem, targetNames) if pShowReport then set addedFiles to addedFiles & ("# " & (last text item of manifestItem) & return) end if set manifestItem to advanceStream(manifestStream) else -- we have an item not in manifest, but in project, so it must be removed from this target. -- display dialog "removing " & targetItem removeTargetFile(targetItem) if pShowReport then set removedFiles to removedFiles & ("# " & targetName & " - " & (last text item of targetItem) & return) end if set targetItem to advanceStream(targetStream) end if end if end repeat end if end repeat -- activate_application(frontmostApplication) -- commit the project changes, and optionally display a report. tell application "CodeWarrior IDE 3.3" -- leave project open for compilation phase? Close Project if pShowReport then if addedFiles is not "" then set theReport to ("# Added files: " & return & addedFiles) if removedFiles is not "" then set theReport to theReport & ("# Removed files: " & return & removedFiles) if (theReport is "") then set theReport to (" # Project is up to date." & return) -- display dialog theReport buttons {"OK"} default button "OK" end if end tell -- restore AppleScript's delimiters. setDelimiters(oldDelimiters) -- return 0 to indicate no error. return theReport end ReconcileProject on run -- when run interactively, -- ask user which project/MANIFEST files to use. set projectPath to (choose file with prompt "Choose a CW Project file." of type {"MMPr"}) as text set manifestPath to (choose file with prompt "Choose a TOC file to process." of type {"TEXT"}) as text ReconcileProject(pSourceTree, projectPath, manifestPath) end run