AbstractSourceCodeManager.st
branchjv
changeset 3838 474d8ec95b33
parent 3420 de9c10ef92a2
parent 3815 36cba9b2a19f
child 3889 58cd8504b5ad
--- a/AbstractSourceCodeManager.st	Tue Feb 04 21:01:56 2014 +0100
+++ b/AbstractSourceCodeManager.st	Wed Apr 01 10:37:40 2015 +0100
@@ -11,6 +11,8 @@
 "
 "{ Package: 'stx:libbasic3' }"
 
+"{ NameSpace: Smalltalk }"
+
 Object subclass:#AbstractSourceCodeManager
 	instanceVariableNames:''
 	classVariableNames:'DefaultManager CachingSources CacheDirectoryName UseWorkTree
@@ -91,11 +93,26 @@
 
     CachingSources isNil ifTrue:[CachingSources := false].
     UseWorkTree    isNil ifTrue:[UseWorkTree := false].
-    CacheDirectoryName isNil ifTrue:[
-        self initCacheDirPath.
-    ].
+
+    self validateCacheDirPath.
+    Smalltalk addDependent:self
 
     "Modified: / 02-03-2012 / 17:00:11 / cg"
+!
+
+update:something with:aParameter from:changedObject
+    "flush resources on language changes"
+
+    something == #returnFromSnapshot ifTrue:[
+        self validateCacheDirPath
+    ]
+!
+
+validateCacheDirPath
+    (CacheDirectoryName isNil 
+    or:[CacheDirectoryName asFilename exists not]) ifTrue:[
+        self initCacheDirPath   
+    ].
 ! !
 
 !AbstractSourceCodeManager class methodsFor:'accessing'!
@@ -565,7 +582,10 @@
     "Created: / 23-08-2006 / 14:05:42 / cg"
 !
 
-revisionLogOf:clsOrNil fromRevision:firstRev toRevision:lastRef fileName:classFileName directory:packageDir module:moduleDir 
+revisionLogOf:clsOrNil 
+    fromRevision:firstRev toRevision:lastRef 
+    fileName:classFileName directory:packageDir module:moduleDir 
+
     ^ self 
         revisionLogOf:clsOrNil
         fromRevision:firstRev
@@ -576,14 +596,55 @@
         module:moduleDir
 !
 
-revisionLogOf:clsOrNil fromRevision:rev1OrNil toRevision:rev2OrNil numberOfRevisions:limitOrNil fileName:classFileName directory:packageDir module:moduleDir 
-    "actually do return a revisionLog. The main worker method.
-     This must be implemented by a concrete source-code manager"
-
-    ^ self subclassResponsibility.
-
-    "Created: 15.11.1995 / 18:12:51 / cg"
-    "Modified: 14.2.1997 / 21:14:01 / cg"
+revisionLogOf:clsOrNil 
+    fromRevision:rev1OrNil toRevision:rev2OrNil numberOfRevisions:limitOrNil 
+    fileName:classFileName directory:packageDir module:moduleDir 
+
+    "Return info about the repository container and (part of) the revisionlog as a collection 
+     of revision entries. Return nil on failure.
+
+     This must be implemented by a concrete source-code manager
+
+     If numRevisionsOrNil is notNil, it limits the number of revision records returned -
+     only numRevions of the newest revision infos will be collected.
+
+     The returned information is a structure (IdentityDictionary)
+     filled with:
+            #container          -> the RCS/CVS container file name 
+            #cvsRoot            -> the CVS root (repository) 
+            #filename           -> the actual source file name
+            #newestRevision     -> the revisionString of the newest revision
+            #numberOfRevisions  -> the number of revisions in the container (nil for all)
+            #revisions          -> collection of per-revision info (see below)
+
+            firstRevOrNil / lastRevOrNil specify from which revisions a logEntry is wanted:
+             -If firstRevOrNil is nil, the first revision is the initial revision
+              otherwise, the log starts with that revision.
+             -If lastRevOrNil is nil, the last revision is the newest revision
+              otherwise, the log ends with that revision.
+
+             -If both are nil, all logEntries are extracted.
+             -If both are 0 (not nil), no logEntries are extracted (i.e. only the header).
+
+            per revision info consists of one record per revision:
+
+              #revision              -> the revision string
+              #author                -> who checked that revision into the repository
+              #date                  -> when was it checked in
+              #state                 -> the RCS state
+              #numberOfChangedLines  -> the number of changed line w.r.t the previous
+              #logMessage            -> the checkIn log message
+
+            revisions are ordered newest first 
+            (i.e. the last entry is for the initial revision; the first for the most recent one)
+            Attention: if state = 'dead' that revision is no longer valid.
+        "
+
+    self subclassResponsibility
+
+    "Created: / 15-11-1995 / 18:12:51 / cg"
+    "Modified: / 14-02-1997 / 21:14:01 / cg"
+    "Modified: / 11-02-2014 / 13:03:11 / Jan Vrany <jan.vrany@fit.cvut.cz>"
 ! !
 
 !AbstractSourceCodeManager class methodsFor:'cache administration'!
@@ -593,7 +654,9 @@
 
     |extractBaseAndVersion versionIsGreater vsnNumberString baseName|
 
-    CacheDirectoryName isNil ifTrue:[^ self].
+    (CacheDirectoryName isNil or:[CacheDirectoryName asFileName isDirectory not]) ifTrue:[
+        ^ self
+    ].
 
     extractBaseAndVersion := 
         [:filenameString |
@@ -910,7 +973,7 @@
 
         SourceCodeManagerUtilities yesToAllNotification isHandled       
         ifTrue:[
-            labels := #('Cancel' 'No' 'Browse' 'Yes to all' 'Yes').
+            labels := #('Cancel' 'No' 'Browse' 'Yes to All' 'Yes').
             actions := #(#cancel false #browse #yesToAll true).
         ] ifFalse:[
             labels := #('Cancel' 'No' 'Browse' 'Yes').
@@ -989,6 +1052,12 @@
      More precisely, this checks that each line starts with zero or more
      tabs (16r9) followed by 0-7 spaces (16r32) followed by non-space non-tab
      character.
+     This is done for two reasons:
+      1) Makefiles (and Make.proto/Make.spec) files MUST not have leading spaces in their rules, but tabs.
+        otherwise, make fails badly.
+
+      2) for diff-comparison, a consisten tab/space discipline avoids false diff-positivies, which resulted from simple
+        edititing with different editors with different tab conventions.
      "
 
     | checkStream |
@@ -1284,7 +1353,7 @@
         lastTop := '/' , top asFilename baseName, '/'.
         idx := containerPath indexOfSubCollection:lastTop.
         idx ~~ 0 ifTrue:[
-            ('SourceCodeManager [warning]: warning: repository path mismatch: ' , (containerPath copyTo:idx-1) , lastTop , ' vs. ' , top , '/') infoPrintCR.
+            ('SourceCodeManager [warning]: repository path mismatch: ' , (containerPath copyTo:idx-1) , lastTop , ' vs. ' , top , '/') infoPrintCR.
             'SourceCodeManager [info]: warning: assuming that mismatch is ok.' infoPrintCR.
             ^ containerPath copyFrom:(idx + lastTop size).
         ]
@@ -1565,7 +1634,7 @@
     ^ newInfo
 
     "
-     self sourceInfoOfClass:Array
+     SourceCodeManager sourceInfoOfClass:Array
     "
 
     "Modified: / 22-10-2008 / 20:49:15 / cg"
@@ -1621,6 +1690,12 @@
     "Created: / 19-01-2012 / 10:39:29 / Jan Vrany <jan.vrany@fit.cvut.cz>"
 !
 
+initialRevisionString
+    "redefinable in subclasses"
+
+    ^ '1.0'
+!
+
 isContainerBased
     "true, if the SCM uses some kind of source container (,v files).
      False, if it is like a database or filesystem."
@@ -1637,7 +1712,7 @@
 !
 
 isExtensionsVersionMethodSelector: selector 
-    "true if the given selector is for one of the manager's extensions version methods.
+    "true if the given selector is for ANY of the manager's extensions version methods.
      These are all named extensionVersion_XXX, where XXX is manager-specific (CVS, P4, SVN etc.).
      For backward compatibility (times, when there was only one CVS sourcecode manager,
      the selector named 'extensionsVersion' alone also counts as a version method
@@ -1828,20 +1903,21 @@
 !
 
 sourceCodeManagerForPackage:aPackageID
-    |module mgr|
+    |defaultManager module mgr|
 
     "JV@2012-01-23: If source code management is disabled, return #(). Following code
      is hack since there is no global boolean flag, sigh"
-    (Smalltalk at:#SourceCodeManager) isNil ifTrue:[ ^ nil ].
+    defaultManager := Smalltalk at:#SourceCodeManager.
+    defaultManager isNil ifTrue:[ "eg. disabled" ^ nil ].
 
     aPackageID notNil ifTrue:[
         "/ see if there is a package-specific manager
         (mgr := self managerForPackage:aPackageID) notNil ifTrue:[^ mgr].
 
-       "/ more or less obsolete now - I know which manager is to be used per package
-"/        self availableManagers do:[:mgr |
-"/            (mgr isResponsibleForPackage:aPackageID) ifTrue:[^ mgr ].
-"/        ].
+        "/ more or less obsolete now - I know which manager is to be used per package
+        "/        self availableManagers do:[:mgr |
+        "/            (mgr isResponsibleForPackage:aPackageID) ifTrue:[^ mgr ].
+        "/        ].
 
         "/ old stuff: see if there is a module-specific manager
         module := aPackageID upTo:$:.
@@ -1849,7 +1925,7 @@
             (mgr isResponsibleForModule:module) ifTrue:[^ mgr ].
         ]
     ].
-    ^ Smalltalk at:#SourceCodeManager
+    ^ defaultManager
 
     "
      self sourceCodeManagerForPackage:'stx:libbasic'.
@@ -1871,6 +1947,11 @@
     "Created: / 21-12-2011 / 18:02:34 / cg"
 !
 
+versionInfoClass
+
+    ^VersionInfo
+!
+
 versionMethodKeyword
 
     "Answers the keyword used by the version management system to
@@ -1955,10 +2036,14 @@
 !AbstractSourceCodeManager class methodsFor:'source code access'!
 
 basicCheckinClass:aClass fileName:classFileName directory:packageDir module:moduleDir logMessage:logMessage force:force
-    "low level checkin of a class into the source repository. Does not deal with anz version method updates,
+    "low level checkin of a class into the source repository. Does not deal with any version method updates,
      only does the checkin, using a temporary file.
      Return true if ok, false if not."
 
+    aClass isPrivate ifTrue:[
+        self reportError:'refuse to check in private classes.'.
+        ^ false.
+    ].
     ^ self 
         withClass:aClass 
         classFileName:classFileName 
@@ -2001,12 +2086,12 @@
      Return true if ok, false if not."
 
     |className answer allLabel allValue 
-     nameOfVersionMethodInClasses|
+     nameOfVersionMethodInClasses revision|
 
     className := aClass name.
     nameOfVersionMethodInClasses := self nameOfVersionMethodInClasses.
 
-    aClass revision isNil ifTrue:[ 
+    (revision := aClass revisionOfManager:self "revision") isNil ifTrue:[ 
         force ifFalse:[
             ('SourceCodeManager [warning]: class ' , className, ' has no revision string') errorPrintCR.
 
@@ -2027,32 +2112,32 @@
                             values:(allValue , #(false #checkIn))
                             default:#checkIn.
             ] ifFalse:[
-                answer := OptionBox 
-                            request:('Class %1 has no revision string.\\Check in as newest ?' bindWith:className allBold) withCRs
-                            label:'Confirm'
-                            buttonLabels:(allLabel , #('Cancel' 'CheckIn' 'Create & CheckIn')) 
-                            values:(allValue , #(false #checkIn #create))
-                            default:#create.
+                force ifTrue:[
+                    revision := self newestRevisionInFile:classFileName directory:packageDir module:moduleDir.
+                    revision isNil ifTrue:[
+                        revision := self initialRevisionString   "/ initial checkin
+                    ].
+                ] ifFalse:[
+                    revision := self initialRevisionString   "/ initial checkin
+                ].
+                answer := #create.
+"/                answer := OptionBox 
+"/                            request:('Class %1 has no revision string.\\Check in as newest ?' bindWith:className allBold) withCRs
+"/                            label:'Confirm'
+"/                            buttonLabels:(allLabel , #('Cancel' 'CheckIn' 'Create & CheckIn')) 
+"/                            values:(allValue , #(false #checkIn #create))
+"/                            default:#create.
             ].
             answer == false ifTrue:[ AbortOperationRequest raise. ^ false ].
             answer == #cancelAll ifTrue:[ AbortAllOperationRequest raise. ^ false ].
             answer == #create ifTrue:[ 
-                self updateVersionMethodOf:aClass for:'$' , 'Header' , '$'.  "/ concatenated to avoid RCS expansion
+                self updateVersionMethodOf:aClass for:(self revisionStringFor:aClass inModule:moduleDir directory:packageDir container:classFileName revision:revision).
             ].
         ]
     ].
 
     "Ensure that the method #version_XXX is present before checking in XXX. 
      It will be missing when checking in classes with only the old method #version"
-"/ this is wrong - it would add the SVN-id as CVS id...
-"/    (aClass theMetaclass includesSelector: nameOfVersionMethodInClasses) ifFalse: [
-"/        versionAsKnownBefore := aClass revisionString.   "/ looks in the old version (non-repository based)
-"/
-"/        self 
-"/            compileVersionMethod:nameOfVersionMethodInClasses 
-"/            of:aClass 
-"/            for:(versionAsKnownBefore ? ('$' , 'Header' , '$')).  "/ concatenated to avoid RCS expansion
-"/    ].
 
     ^ self basicCheckinClass:aClass fileName:classFileName directory:packageDir module:moduleDir logMessage:logMessage force:force.
 
@@ -2106,7 +2191,7 @@
             directory:packageDir
             module:moduleDir.
     s isNil ifTrue:[^ nil].
-    contents := s contentsOfEntireFile.
+    contents := s contentsAsString.
     s close.
     ^ contents
 
@@ -2332,12 +2417,11 @@
     "Modified: / 29-09-2011 / 22:02:55 / cg"
 !
 
-loadPackageWithId: aPackageId fromRepositoryAsAutoloaded: doLoadAsAutoloaded
-
+loadPackageWithId:aPackageId fromRepositoryAsAutoloaded: doLoadAsAutoloaded
     "Should be redefined by sub classes.
-     Return true if loaded, false otherwise."
-
-    ^ false
+     Raise an exception, if load failed."
+
+    PackageLoadError raiseRequestWith:aPackageId.
 !
 
 streamForFile:fileName revision:revision directory:packageDir module:moduleDir 
@@ -2560,6 +2644,36 @@
     "
 !
 
+extractKeyValueFor:key fromRevisionString:aString 
+    "{ Pragma: +optSpace }"
+
+    "extract a particular value from a string which has the format:
+        key1: value1, key2: value2, .... keyN: valueN"
+
+    |value idx1 idx2|
+
+    "/ 'Path: stx/libbasic/Array.st, Version: 123, User: cg, Time: 2011-12-21T21:03:08.826'
+
+    idx1 := aString indexOfSubCollection:(key,': ').
+    idx1 ~~ 0 ifTrue:[
+        idx1 := idx1 + (key,': ') size.
+        idx2 := aString indexOfSubCollection:', ' startingAt:idx1.
+        idx2 == 0 ifTrue:[ idx2 := aString size + 1 ].
+        value := aString copyFrom:idx1 to:idx2-1.     
+    ].
+    ^ value
+
+    "
+     self 
+        extractKeyValueFor:'Path' 
+        fromRevisionString:'Path: stx/libbasic/Array.st, Version: 123, User: cg, Time: 2011-12-21T21:03:08.826'
+
+     self 
+        extractKeyValueFor:'Time' 
+        fromRevisionString:'Path: stx/libbasic/Array.st, Version: 123, User: cg, Time: 2011-12-21T21:03:08.826'
+    "
+!
+
 fileOutSourceCodeExtensions: extensions package: package on: stream
     "File out extension methods for given package on stream. 
      Not programming-language safe  - can handle smalltalk methods."
@@ -2618,16 +2732,28 @@
     ].
 
     self withSourceRewriteHandlerDo:[
-        aClass fileOutOn:aStream 
-               withTimeStamp:withTimeStamp 
-               withInitialize:withInitialize 
-               withDefinition:withDefinition
-               methodFilter:filter.
+        | writer |
+
+        writer := aClass programmingLanguage sourceFileWriterClass new.
+        writer generatingSourceForOriginal:true.
+        writer 
+            fileOut:aClass 
+            on:aStream 
+            withTimeStamp:withTimeStamp 
+            withInitialize:withInitialize 
+            withDefinition:withDefinition 
+            methodFilter:methodFilter 
+
+"/        aClass fileOutOn:aStream 
+"/               withTimeStamp:withTimeStamp 
+"/               withInitialize:withInitialize 
+"/               withDefinition:withDefinition
+"/               methodFilter:filter.
     ].
 
     self checkTabSpaceConventionIn: aStream.
 
-    "Modified: / 29-11-2013 / 12:01:38 / Jan Vrany <jan.vrany@fit.cvut.cz>"
+    "Modified: / 22-01-2015 / 09:39:28 / Jan Vrany <jan.vrany@fit.cvut.cz>"
 !
 
 getExistingContainersInModule:aModule directory:aPackage
@@ -2847,57 +2973,24 @@
      This extracts the relevant info from aString which is in the format as created
      by standardRevisionStringFor:...."
 
-    |info path version user ts timeStamp idx1 idx2|
+    |info path version user timeStamp|
 
     "/ 'Path: stx/libbasic/Array.st, Version: 123, User: cg, Time: 2011-12-21T21:03:08.826'
 
-    idx1 := aString indexOfSubCollection:'Path: '.
-    idx1 ~~ 0 ifTrue:[
-        idx1 := idx1 + 'Path: ' size.
-        idx2 := aString indexOfSubCollection:', ' startingAt:idx1.
-        path := aString copyFrom:idx1 to:idx2-1.     
-    ].
-    idx1 := aString indexOfSubCollection:'Version: '.
-    idx1 ~~ 0 ifTrue:[
-        idx1 := idx1 + 'Version: ' size.
-        idx2 := aString indexOfSubCollection:', ' startingAt:idx1.
-        idx2 == 0 ifTrue:[
-            version := aString copyFrom:idx1     
-        ] ifFalse:[
-            version := aString copyFrom:idx1 to:idx2-1.     
-        ].
-    ].
-    idx1 := aString indexOfSubCollection:'User: '.
-    idx1 ~~ 0 ifTrue:[
-        idx1 := idx1 + 'User: ' size.
-        idx2 := aString indexOfSubCollection:', ' startingAt:idx1.
-        idx2 == 0 ifTrue:[
-            user := aString copyFrom:idx1     
-        ] ifFalse:[
-            user := aString copyFrom:idx1 to:idx2-1.     
-        ].
-    ].
-    idx1 := aString indexOfSubCollection:'Time: '.
-    idx1 ~~ 0 ifTrue:[
-        idx1 := idx1 + 'Time: ' size.
-        idx2 := aString indexOfSubCollection:', ' startingAt:idx1.
-        idx2 == 0 ifTrue:[
-            ts := aString copyFrom:idx1     
-        ] ifFalse:[
-            ts := aString copyFrom:idx1 to:idx2-1.     
-        ].
-        timeStamp := Timestamp readIso8601FormatFrom:ts
-    ].
-
-    info := VersionInfo new.
+    path := self extractKeyValueFor:'Path' fromRevisionString:aString.
+    version := self extractKeyValueFor:'Version' fromRevisionString:aString.
+    user := self extractKeyValueFor:'User' fromRevisionString:aString.
+    timeStamp := self extractKeyValueFor:'Time' fromRevisionString:aString.
+
+    info := self versionInfoClass new.
     path notNil ifTrue:[ info fileName:(path asFilename baseName) ].
     info revision:version.
     user notNil ifTrue:[ info user:user ].
-    timeStamp notNil ifTrue:[ info timeStamp:timeStamp ].
+    timeStamp notNil ifTrue:[ info timeStamp:(Timestamp readFrom:timeStamp) ].
     ^ info
 
     "
-     self revisionInfoFromString:'Path: stx/libbasic/Array.st, Version: 123, User: cg, Time: 2011-12-21T21:03:08.826' 
+     self revisionInfoFromStandardVersionString:'Path: stx/libbasic/Array.st, Version: 123, User: cg, Time: 2011-12-21T21:03:08.826' 
     "
 
     "Created: / 23-07-2012 / 18:45:41 / cg"
@@ -3313,6 +3406,22 @@
     "Created: / 23-08-2006 / 14:14:59 / cg"
 !
 
+revisionStringFor:aClass inModule:moduleDir directory:packageDir container:fileName revision:revisionString
+    "utility function: return a string usable as initial revision string.
+     Can be redefined in subclasses"
+
+    ^ self standardRevisionStringFor:aClass inModule:moduleDir directory:packageDir container:fileName revision:revisionString
+
+    "
+     self 
+        revisionStringFor:Array 
+        inModule:'stx' 
+        directory:'libbasic' 
+        container:'Array.st' 
+        revision:'123'          
+    "
+!
+
 revisionsOf:aClass
     "return a collection of revisions (as strings) found in the repository.
      The most recent (newest) revision will be the first in the list.
@@ -3360,7 +3469,8 @@
 
 withSourceRewriteHandlerDo:aBlock
     "hook for just-in-time rewriting of a method's sourceCode while filing out
-     used when saving version_XXX methods in a non-XXX sourceCodeManager"
+     used when saving version_XXX methods in a non-XXX sourceCodeManager,
+     or when generating sourcecode for another Smalltalk system (VSE fileout)"
 
     AbstractSourceFileWriter methodSourceRewriteQuery handle:[:rewriteQuery |
         |m newSource selector|
@@ -3391,7 +3501,9 @@
     "Modified: / 27-09-2011 / 16:48:58 / Jan Vrany <jan.vrany@fit.cvut.cz>"
 !
 
-writeHistoryLogSince:timeGoal filterSTSources:filterSTSourcesBoolean filterUser:userFilter filterRepository:repositoryFilter filterModules:moduleFilter filterProjects:projectFilterArg to:aStream
+writeHistoryLogSince:timeGoal filterSTSources:filterSTSourcesBoolean 
+        filterUser:userFilter filterRepository:repositoryFilter filterModules:moduleFilter 
+        filterProjects:projectFilterArg to:aStream
     "send a full historyLog to some stream.
      This walks over all possible repository roots."
 
@@ -3448,7 +3560,8 @@
             pkg := (PackageId module:module directory:directory) asString.
 
             (projectFilter isEmptyOrNil
-            or:[ projectFilter includes:pkg ]) ifTrue:[
+                or:[ projectFilter contains:[:pat | pat match:pkg caseSensitive:false] ]
+            ) ifTrue:[
 
                 user := info at:#user ifAbsent:'?'.
                 recordType := info at:#cvsRecordType ifAbsent:'?'.
@@ -3873,20 +3986,15 @@
 !AbstractSourceCodeManager class methodsFor:'documentation'!
 
 version
-    ^ '$Header: /cvs/stx/stx/libbasic3/AbstractSourceCodeManager.st,v 1.318 2013-11-14 15:34:21 stefan Exp $'
+    ^ '$Header: /cvs/stx/stx/libbasic3/AbstractSourceCodeManager.st,v 1.341 2015-02-28 23:42:40 cg Exp $'
 !
 
 version_CVS
-    ^ '$Header: /cvs/stx/stx/libbasic3/AbstractSourceCodeManager.st,v 1.318 2013-11-14 15:34:21 stefan Exp $'
-!
-
-version_HG
-
-    ^ '$Changeset: <not expanded> $'
+    ^ '$Header: /cvs/stx/stx/libbasic3/AbstractSourceCodeManager.st,v 1.341 2015-02-28 23:42:40 cg Exp $'
 !
 
 version_SVN
-    ^ '$Id: AbstractSourceCodeManager.st,v 1.318 2013-11-14 15:34:21 stefan Exp $'
+    ^ '$Id: AbstractSourceCodeManager.st,v 1.341 2015-02-28 23:42:40 cg Exp $'
 ! !