stx/MetacelloStXPackageSpec.st
author Jan Vrany <jan.vrany@fit.cvut.cz>
Tue, 18 Sep 2012 18:26:49 +0000
changeset 18 d2f3f631bc3c
parent 9 d126d2954bf9
child 21 8dd3045fbf6e
permissions -rw-r--r--
- MetacelloStXVersionSpec class definition added: #version_SVN - MetacelloStXVersionConstructor class definition added: #version_SVN - MetacelloStXPackageSpec class definition added:78 methods - MetacelloStXProject class definition added: #packageSpecClass #versionConstructorClass #versionSpecClass #version_SVN - stx_goodies_metacello_stx changed: #classNamesAndAttributes #extensionMethodNames #preRequisites

"{ Package: 'stx:goodies/metacello/stx' }"

MetacelloAbstractPackageSpec subclass:#MetacelloStXPackageSpec
	instanceVariableNames:'file repositories goferPackage preLoadDoIt postLoadDoIt'
	classVariableNames:''
	poolDictionaries:''
	category:'Metacello-St/X-Specs'
!

Collection subclass:#Prerequisites
	instanceVariableNames:'packageSpec suppressions additions'
	classVariableNames:''
	poolDictionaries:''
	privateIn:MetacelloStXPackageSpec
!


!MetacelloStXPackageSpec methodsFor:'accessing'!

file: aString

	file := aString
!

getFile
	"raw access to iv"
	
	^file
!

getName
    "raw access to iv"

    ^ name
!

getRepositories
    "raw access to iv"

    ^ repositories
!

goferLoaderReference

	^file == nil 
		ifTrue: [ GoferPackageReference name: self name ]
		ifFalse: [ 
			"does Monticello-style #versionInfoFromVersionNamed: matching"
			MetacelloGoferPackage name: self name packageFilename: self file ]
!

goferPackage

	goferPackage == nil 
		ifTrue: [ goferPackage := MetacelloGoferPackage name: self name packageFilename: self file ].
	^goferPackage
!

info
	"test compatibility method"

	^self
!

preLoadDoIt: anObject

	anObject setPreLoadDoItInMetacelloSpec: self
!

repository: aStringOrMetacelloRepositorySpec

	self repositories repository: aStringOrMetacelloRepositorySpec
!

repository: aString username: username password: password

	self repositories repository: aString username: username password: password
!

setPostLoadDoIt: aSymbol

	postLoadDoIt := aSymbol
!

setPreLoadDoIt: aSymbol

	preLoadDoIt := aSymbol
! !

!MetacelloStXPackageSpec methodsFor:'construction'!

file: aString constructor: aVersionConstructor
    aVersionConstructor fileForPackage: aString
!

includes: anObject constructor: aVersionConstructor
    aVersionConstructor includesForPackage: anObject
!

postLoadDoIt: aSymbol constructor: aVersionConstructor
    aVersionConstructor postLoadDoItForPackage: aSymbol
!

preLoadDoIt: aSymbol constructor: aVersionConstructor
    aVersionConstructor preLoadDoItForPackage: aSymbol
!

repositories: aBlock constructor: aVersionConstructor
    aVersionConstructor repositoriesForPackage: aBlock
!

repository: anObject constructor: aVersionConstructor
    aVersionConstructor repositoryForPackage: anObject
!

repository: aString username: username password: password constructor: aVersionConstructor
    aVersionConstructor repositoryForPackage: aString username: username password: password
!

requires: anObject constructor: aVersionConstructor
    aVersionConstructor requiresForPackage: anObject
!

supplyingAnswers: anObject constructor: aVersionConstructor
    aVersionConstructor supplyingAnswersForPackage: anObject
! !

!MetacelloStXPackageSpec methodsFor:'copying'!

postCopy

	super postCopy.
	goferPackage := nil.
	repositories := repositories copy.
! !

!MetacelloStXPackageSpec methodsFor:'development support'!

copySpecTo: aRepositorySpec
	"Copy current mcz file to the repository named in aRepositorySpec"

	self loader copySpec: self from: self repositorySpecs to: aRepositorySpec createRepository
!

forceUpdatePackageSpec: updatedSpecs using: anMCLoader
	
	self updatePackageSpec: updatedSpecs force: true using: anMCLoader
!

goferBranchPackage: branchName message: commitMessage
	"uses gofer to do commit ... non-interactive"

	| latestFile pkgSpec   |
	(file notNil and: [ (self name, '.', branchName) = self file ])
		ifTrue: [ 
			latestFile := self loader latestPackage: self file fromRepository: self repositorySpecs.
			pkgSpec := self copy.
			latestFile ~~ nil
				ifTrue: [ 
					pkgSpec file: latestFile.
					pkgSpec goferCommitPackage: commitMessage.
					^ self ] ].
	self loader goferCommitBranchPackage: branchName using: self repositorySpecs commitMessage: commitMessage
!

goferCommitPackage: commitMessage
	"uses gofer to do commit ... non-interactive"
	
	| latestFile pkgSpec |
	^(file notNil and: [ self name = self file ])
		ifTrue: [
			latestFile := self loader latestPackage: self name fromRepository: self repositorySpecs.
			pkgSpec := self copy.
			pkgSpec file: latestFile.
			pkgSpec goferCommitPackage: commitMessage ]
		ifFalse: [ self loader goferCommitPackageUsing: self repositorySpecs commitMessage: commitMessage ]
!

packagesNeedSavingVisited: visitedProjects using: repos into: aCollection

	^self loader packagesNeedSavingUsing: repos into: aCollection
!

savePackage
	"Interactive save ... prompted for commit message and package name unless MCVersionNameAndMessageRequest handled"

	| latestFile pkgSpec |
	^(file notNil and: [ self name = self file ])
		ifTrue: [
			latestFile := self loader latestPackage: self name fromRepository: self repositorySpecs.
			pkgSpec := self copy.
			pkgSpec file: latestFile.
			pkgSpec savePackage ]
		ifFalse: [ self loader savePackageUsing: self repositorySpecs ]
!

updateForSpawnMethod: sourceSpec
	"This means that this spec was used in a baseline and will be used in a version .... drop all information that isn't useful"

	| nm fl |
	nm := name.
	fl := file.
	sourceSpec ~~ nil ifTrue: [ fl := sourceSpec file ].
	super updateForSpawnMethod: sourceSpec.
	file := repositories := goferPackage := preLoadDoIt := postLoadDoIt := nil.
	name := nm.
	file := fl.
!

updatePackageRepositories: repositorySpecs

	| resolvedPackageRef |
	Transcript cr; show: '  Looking up version -> ', self file.
	resolvedPackageRef := self loader resolveSpec: self from: repositorySpecs.
	Transcript cr; show: 'Update repositoryGroup -> ', resolvedPackageRef name, ' ' , resolvedPackageRef repository description.
	resolvedPackageRef version workingCopy repositoryGroup addRepository: resolvedPackageRef repository
!

updatePackageRepositoriesFor: aVersionSpec
	"Don't update the repository unless the package is loaded in the image"
	
	self workingCopy == nil ifTrue: [ ^self ].
	self updatePackageRepositories: self repositorySpecs, aVersionSpec repositorySpecs.	
!

updatePackageSpec: updatedSpecs force: force using: anMCLoader
	"Add pkg copy to updatedSpecs if the file in current image is different from the receiver's file"

	| viName |
	(force not and: [ self getFile == nil ])
		ifTrue: [ ^ self ].	"no file explicitly specified in this spec"
	(viName := self workingCopyNameFor: anMCLoader) == nil
		ifTrue: [ ^ self ].	"no working copy"
	viName ~= self file
		ifTrue: [ 
			| spec |
			spec := self copy.
			spec file: viName.
			updatedSpecs at: spec name put: spec ]
		ifFalse: [ updatedSpecs at: self name put: #uptodate ]
!

updatePackageSpec: updatedSpecs using: anMCLoader
	"Add pkg copy to updatedSpecs if the file in current image is different from the receiver's file"
	
	self updatePackageSpec: updatedSpecs force: false using: anMCLoader
! !

!MetacelloStXPackageSpec methodsFor:'loading'!

ensureLoadUsing: mcLoader
	self explicitLoadUsing: mcLoader ensureSpecLoader
!

ensureLoadedForDevelopmentUsing: mcLoader
	"noop"
	
	^true
!

explicitLoadUsing: mcLoader

	| wc fetchingSpecLoader |
	((wc := self workingCopy) ~~ nil and: [ wc needsSaving ]) 
		ifTrue: [ 
			(MetacelloSkipDirtyPackageLoad signal: self)
				ifTrue:  [
					Transcript cr; show: 'Skipping load of modified package: ', self file.
					^self]
				ifFalse: [Transcript cr; show: 'Load over modified package: ', self file] ].
	"fetch and explicitly load it"
	fetchingSpecLoader := mcLoader fetchingSpecLoader.
	fetchingSpecLoader
		explicitLoadPackageSpecs: (Array with: self) 
		repositories: (fetchingSpecLoader repositoriesFrom: self repositorySpecs).
!

fetch

	self fetchUsing: self loader
!

fetchPackage: aLoaderPolicy

	self fetchUsing: 
		(self loader
			loaderPolicy: aLoaderPolicy;
			yourself)
!

fetchUsing: mcLoader

	| fetchingSpecLoader |
	fetchingSpecLoader := mcLoader fetchingSpecLoader.
	fetchingSpecLoader
		linearLoadPackageSpecs: (Array with: self) 
		repositories: (fetchingSpecLoader repositoriesFrom: self repositorySpecs).
!

load

	self explicitLoadUsing: self loader
!

loadUsing: mcLoader

	self loader doingLoads: [ self explicitLoadUsing: mcLoader ]
!

loadUsing: aLoader gofer: gofer

	^aLoader linearLoadPackageSpec: self gofer: gofer
!

packageSpecsInLoadOrder

	^{ self. }
!

postLoadDoIt: anObject

	anObject setPostLoadDoItInMetacelloSpec: self
!

repositoryDescriptions
    ^ self repositorySpecs collect: [ :repoSpec | repoSpec description ]
!

repositorySpecs

	^self repositories map values 
! !

!MetacelloStXPackageSpec methodsFor:'merging'!

mergeMap

	| map |
	map := super mergeMap.
	map at: #file put: file.
	map at: #repositories put: self repositories.
	map at: #preLoadDoIt put: preLoadDoIt.
	map at: #postLoadDoIt put: postLoadDoIt.
	^map
!

mergeSpec: anotherSpec

	| newSpec map anotherPackages anotherRepositories |
	newSpec := super mergeSpec: anotherSpec.
	map := anotherSpec mergeMap.
	(anotherRepositories := map at: #repositories) notEmpty
		ifTrue: [ 
			newSpec 
				repositories: (self repositories isEmpty
					ifTrue: [ anotherRepositories ]
					ifFalse: [ self repositories mergeSpec: anotherRepositories ]) ].
	^newSpec
!

nonOverridable

	^super nonOverridable, #( repositories)
! !

!MetacelloStXPackageSpec methodsFor:'printing'!

configMethodBodyOn: aStream hasName: hasName indent: indent

	| hasFile hasRepositories hasPreLoadDoIt hasPostLoadDoIt hasRequiresOrIncludesOrAnswers |
	hasFile := file ~~ nil.
	hasRepositories := self repositorySpecs size > 0.
	hasPreLoadDoIt := self getPreLoadDoIt ~~ nil.
	hasPostLoadDoIt := self getPostLoadDoIt ~~ nil.
	hasRequiresOrIncludesOrAnswers := (self requires isEmpty and: [ self includes isEmpty and: [self answers isEmpty ]]) not.
	hasRequiresOrIncludesOrAnswers
		ifTrue: [ 
			self 
				configMethodBodyOn: aStream 
				hasName: hasName 
				cascading: hasFile | hasRepositories | hasPreLoadDoIt | hasPostLoadDoIt
				indent: indent ].
	self 
		configMethodOn: aStream 
		for: file 
		selector: 'file: ' 
		cascading: hasName | hasRepositories | hasPreLoadDoIt | hasPostLoadDoIt | hasRequiresOrIncludesOrAnswers 
		cascade: hasRepositories | hasPreLoadDoIt | hasPostLoadDoIt 
		indent: indent.
	hasRepositories
		ifTrue: [ 
			(self repositorySpecs size > 1)
				ifTrue: [ 
					hasName | hasFile | hasPreLoadDoIt | hasPostLoadDoIt | hasRequiresOrIncludesOrAnswers
						ifTrue: [ 
							aStream cr;
							tab: indent. ].
					aStream 
						nextPutAll: 'repositories: ['; 
						cr;
						tab: indent + 1;
						nextPutAll: 'spec'; 
						cr.
					self repositories configMethodCascadeOn: aStream indent: indent + 1.
					aStream nextPutAll: ' ]' ]
				ifFalse: [ 
					hasName | hasFile | hasPreLoadDoIt | hasPostLoadDoIt | hasRequiresOrIncludesOrAnswers
						ifTrue: [ aStream cr; tab: indent ].
					self repositories configMethodCascadeOn: aStream indent: indent ].
			hasPreLoadDoIt | hasPostLoadDoIt ifTrue: [ aStream nextPut: $; ] ].
	self 
		configMethodOn: aStream 
		for: self getPreLoadDoIt 
		selector: 'preLoadDoIt: '
		cascading: hasName | hasFile | hasRepositories | hasPostLoadDoIt | hasRequiresOrIncludesOrAnswers 
		cascade: hasPostLoadDoIt 
		indent: indent.
	self 
		configMethodOn: aStream 
		for: self getPostLoadDoIt 
		selector: 'postLoadDoIt: ' 
		cascading: hasName | hasFile | hasRepositories | hasPreLoadDoIt | hasRequiresOrIncludesOrAnswers 
		cascade: false 
		indent: indent.
	aStream nextPut: $.
!

configMethodCascadeOn: aStream member: aMember last: lastCascade indent: indent

	aMember methodUpdateSelector  == #remove:
		ifTrue: [ aStream  nextPutAll: 'removePackage: ', self name printString ]
		ifFalse: [ self configShortCutMethodBodyOn: aStream member: aMember indent: indent ].
	lastCascade
		ifTrue: [ aStream nextPut: $. ]
		ifFalse: [ aStream nextPut: $;; cr ]
!

configMethodOn: aStream indent: indent

	| hasRepositories hasPreLoadDoIt hasPostLoadDoIt hasRequiresOrIncludesOrAnswers hasFile |
	hasFile := file ~~ nil.
	hasRepositories := self repositorySpecs size > 0.
	hasPreLoadDoIt := self getPreLoadDoIt ~~ nil.
	hasPostLoadDoIt := self getPostLoadDoIt ~~ nil.
	hasRequiresOrIncludesOrAnswers := (self requires isEmpty and: [ self includes isEmpty and: [self answers isEmpty ]]) not.
	aStream tab: indent; nextPutAll: 'spec '.
	hasFile | hasRepositories | hasPreLoadDoIt | hasPostLoadDoIt | hasRequiresOrIncludesOrAnswers
		ifTrue: [
			aStream cr; tab: indent + 1; nextPutAll: 'name: ', self name printString; nextPut: $;.
			self configMethodBodyOn: aStream hasName: true indent: indent + 1 ]
		ifFalse: [
			aStream nextPutAll: 'name: ', self name printString ]
!

configShortCutMethodBodyOn: aStream member: aMember indent: indent

	| hasFile hasRepositories hasPreLoadDoIt hasPostLoadDoIt hasRequiresOrIncludesOrAnswers |
	hasFile := file ~~ nil.
	hasRepositories := self repositorySpecs size > 0.
	hasPreLoadDoIt := self getPreLoadDoIt ~~ nil.
	hasPostLoadDoIt := self getPostLoadDoIt ~~ nil.
	hasRequiresOrIncludesOrAnswers := (self requires isEmpty and: [ self includes isEmpty and: [self answers isEmpty ]]) not.
	hasRepositories | hasPreLoadDoIt | hasPostLoadDoIt | hasRequiresOrIncludesOrAnswers
		ifTrue: [
			aStream 
				nextPutAll: 'package: ', self name printString, ' ';
				nextPutAll: aMember methodUpdateSelector asString, ' ['; cr.
			aStream tab: indent + 1; nextPutAll: 'spec '.
			self configMethodBodyOn: aStream hasName: false indent: indent + 2.
			aStream nextPutAll: ' ]'.
			^self ].
	aStream nextPutAll: 'package: ', self name printString.
	hasFile
		ifTrue: [ aStream nextPutAll: ' with: ', file printString ]
! !

!MetacelloStXPackageSpec methodsFor:'private'!

extractNameFromFile

	file == nil ifTrue: [ ^nil ].
	^(self loader nameComponentsFrom: self file) first
!

includesForPackageOrdering

	^self includes
!

resolveToPackagesIn: aVersionSpec visited: visited

	^{ self }
!

setRequires: aCollection

    self breakPoint: #jv.

    requires := aCollection

    "Created: / 05-09-2012 / 19:47:18 / Jan Vrany <jan.vrany@fit.cvut.cz>"
! !

!MetacelloStXPackageSpec methodsFor:'querying'!

file

	file == nil ifTrue: [ ^self name ].
	^file
!

getPostLoadDoIt

	^postLoadDoIt
!

getPreLoadDoIt

	^preLoadDoIt
!

isPackageLoaded

	^self isPackageLoaded: self loader
!

isPackageLoaded: aLoader

	^(self workingCopyNameFor: aLoader) ~~ nil
!

name

	name == nil ifTrue: [ name := self extractNameFromFile ].
	^name
!

postLoadDoIt

	^postLoadDoIt
!

preLoadDoIt

	^preLoadDoIt
!

repositories

	repositories == nil ifTrue: [ repositories := self project repositoriesSpec ].
	^ repositories
!

repositories: anObject
	repositories := anObject
!

repository
    self deprecated: 'Use repositories or repositorySpecs'.
    self repositorySpecs isEmpty
        ifTrue: [ ^ nil ].
    ^ self repositorySpecs first
!

requires

    requires == nil ifTrue: [ 
        requires := Prerequisites for: self. 
    ].
    ^requires

    "Created: / 05-09-2012 / 19:47:56 / Jan Vrany <jan.vrany@fit.cvut.cz>"
! !

!MetacelloStXPackageSpec methodsFor:'testing'!

compareCurrentVersion: anOperator targetVersionStatus: statusIgnored using: anMCLoader

	self 
		currentPackageLoaded: [:bool | ^bool ] 
		comparing: anOperator 
		notLoaded: [:ignored | ^false ]
		using: anMCLoader
!

compareRelativeCurrentVersion: anOperator targetVersionStatus: statusIgnored using: anMCLoader

	^self compareCurrentVersion: anOperator targetVersionStatus: statusIgnored using: anMCLoader
!

compareWorkingCopyNamed: wcName using: comarisonOperator
	| fileRef wcRef |
	fileRef := GoferResolvedReference name: self file.
	wcRef := GoferResolvedReference name: wcName.
	^ wcRef compare: fileRef using: comarisonOperator
!

currentPackageLoaded: loadedBlock comparing: comarisonOperator notLoaded: notLoadedBlock using: anMCLoader
	"Use currentVersionInfoFor: because it involves the loader and returns versionInfo for a planned load 
	 (atomic loaders) or currently loaded package"

	| wcName vis |
	vis := anMCLoader ancestorsFor: self.
	vis notNil
		ifTrue: [ 
			| fileRef wcRef |
			self getFile == nil
				ifTrue: [ ^ loadedBlock value: false ].
			vis
				do: [ :vi | 
					wcName := vi name.
					fileRef := GoferResolvedReference name: self file.
					wcRef := GoferResolvedReference name: wcName.
					(wcRef compare: fileRef using: comarisonOperator)
						ifTrue: [ ^ loadedBlock value: true ] ].
			^ loadedBlock value: false ].
	^ notLoadedBlock value: true
!

currentPackageLoaded: loadedBlock notLoaded: notLoadedBlock using: anMCLoader
	"Use currentVersionInfoFor: because it involves the loader and returns versionInfo for a planned load 
	 (atomic loaders) or currently loaded package"

	| wcName vis |
	vis := anMCLoader ancestorsFor: self.
	vis notNil
		ifTrue: [ 
			self getFile == nil
				ifTrue: [ ^ loadedBlock value: #() value: self file ].
			^ loadedBlock value: vis value: self file ].
	^ notLoadedBlock value
!

hasRepository
    ^ self repositorySpecs notEmpty
! !

!MetacelloStXPackageSpec methodsFor:'visiting'!

projectDo: projectBlock packageDo: packageBlock groupDo: groupBlock

	packageBlock value: self
!

visitingWithPackages: packages

	packages at: self name put: self
! !

!MetacelloStXPackageSpec::Prerequisites class methodsFor:'instance creation'!

for: spec
    ^self new initializeFor: spec

    "Created: / 05-09-2012 / 19:49:34 / Jan Vrany <jan.vrany@fit.cvut.cz>"
! !

!MetacelloStXPackageSpec::Prerequisites methodsFor:'adding & removing'!

add:anObject
    "add the argument, anObject to the receiver.
     If the receiver is ordered, the position of the new element is undefined
     (i.e. don't depend on where it will be put).
     An error is raised here - it is to be implemented by a concrete subclass."

    ^ self shouldImplement
!

addFirst:anObject
    "add the argument, anObject to the receiver.
     If the receiver is ordered, the new element will be added at the beginning.
     An error is raised here - it is to be implemented by a concrete subclass."

    ^ self shouldImplement
!

remove:anObject ifAbsent:exceptionBlock
    "search for the first element, which is equal to anObject;
     if found, remove and return it.
     If not found, return the the value of the exceptionBlock.
     Uses equality compare (=) to search for the occurrence.
     An error is raised here - it is to be implemented by a concrete subclass."

    ^ self shouldImplement
!

removeIdentical:anObject ifAbsent:exceptionBlock
    "search for the first element, which is identical to anObject;
     if found, remove and return it.
     If not found, return the the value of the exceptionBlock.
     Uses identity compare (==) to search for the occurrence.
     An error is raised here - it is to be implemented by a concrete subclass."

    ^ self shouldImplement
!

removeLast
    "remove the last element from the receiver.
     Return the removed element.
     An error is raised here - it is to be implemented by a concrete subclass."

    ^ self shouldImplement
! !

!MetacelloStXPackageSpec::Prerequisites methodsFor:'enumerating'!

do:aBlock
    "evaluate the argument, aBlock for each element"

    | def |

    def := ProjectDefinition definitionClassForPackage: packageSpec name.
    def preRequisites do:[:each|
        aBlock value: each
    ]

    "Modified: / 05-09-2012 / 19:53:08 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!

reverseDo:aBlock
    "evaluate the argument, aBlock for each element in reverse order."

    ^ self do: aBlock

    "Modified: / 05-09-2012 / 19:51:24 / Jan Vrany <jan.vrany@fit.cvut.cz>"
! !

!MetacelloStXPackageSpec::Prerequisites methodsFor:'growing'!

grow:howBig
    "change the receivers size"

    ^ self shouldImplement
! !

!MetacelloStXPackageSpec::Prerequisites methodsFor:'initialization'!

initializeFor: aMetacelloStXPackageSpec
    packageSpec := aMetacelloStXPackageSpec

    "Created: / 05-09-2012 / 19:48:48 / Jan Vrany <jan.vrany@fit.cvut.cz>"
! !

!MetacelloStXPackageSpec class methodsFor:'documentation'!

version_SVN
    ^ '$Id::                                                                                                                        $'
! !