SystemOrganization addCategory: #'Package-Dependencies'! Object subclass: #PDPackage instanceVariableNames: 'dependencies packageInfo included' classVariableNames: '' poolDictionaries: '' category: 'Package-Dependencies'! !PDPackage class methodsFor: 'instance creation' stamp: 'jf 10/7/2008 09:12'! new self shouldNotImplement! ! !PDPackage class methodsFor: 'instance creation' stamp: 'jf 10/7/2008 09:12'! on: aPackageInfo ^ self basicNew initializeWithPackage: aPackageInfo; yourself! ! !PDPackage methodsFor: 'accessing' stamp: 'jf 10/7/2008 09:22'! add: aDependency ^ dependencies add: aDependency! ! !PDPackage methodsFor: 'accessing' stamp: 'jf 10/7/2008 09:49'! beIncluded included := true! ! !PDPackage methodsFor: 'accessing' stamp: 'jf 10/7/2008 09:09'! dependencies ^ dependencies! ! !PDPackage methodsFor: 'testing' stamp: 'jf 10/7/2008 09:37'! hasCyclicDependencies ^ self dependencies anySatisfy: [ :each | each isCyclic ]! ! !PDPackage methodsFor: 'accessing' stamp: 'jf 10/7/2008 09:20'! info ^ packageInfo! ! !PDPackage methodsFor: 'initialization' stamp: 'jf 10/7/2008 09:48'! initializeWithPackage: aPackageInfo self initialize. packageInfo := aPackageInfo. dependencies := Bag new. included := false.! ! !PDPackage methodsFor: 'testing' stamp: 'jf 10/7/2008 09:49'! isIncluded ^ included! ! !PDPackage methodsFor: 'accessing' stamp: 'jf 10/7/2008 10:01'! packageName ^ self info packageName! ! Object subclass: #PDPackageAnalyzer instanceVariableNames: 'relation' classVariableNames: '' poolDictionaries: '' category: 'Package-Dependencies'! !PDPackageAnalyzer class methodsFor: 'examples' stamp: 'lr 4/3/2008 19:11'! allMonticelloPackageNames ^ MCWorkingCopy allManagers collect: [ :each | each packageInfo packageName ]! ! !PDPackageAnalyzer class methodsFor: 'examples' stamp: 'lr 4/3/2008 19:12'! allPackageNames ^ PackageOrganizer default packages collect: [ :each | each packageName ]! ! !PDPackageAnalyzer class methodsFor: 'examples' stamp: 'pmm 9/25/2008 09:07'! examples (self onPackagesNamed: self seasidePackageNames) save: 'seaside.dot'. (self onPackagesNamed: self omnibrowserPackageNames) save: 'ob.dot'. (self onPackagesNamed: self allMonticelloPackageNames) save: 'monticello.dot'. (self onPackagesNamed: self allPackageNames) save: 'squeak.dot'. (self onPackagesNamed: self pierPackageNames) save: 'pier.dot'! ! !PDPackageAnalyzer class methodsFor: 'examples' stamp: 'lr 1/9/2008 14:29'! omnibrowserPackageNames ^ #( 'Bogus' 'BogusExt' 'BogusInfo' 'OB-Fake' 'OB-Monticello' 'OB-Morphic' 'OB-Refactory' 'OB-Standard' 'OB-Tests-Core' 'OB-Tests-Morphic' 'OB-Tests-Standard' 'OB-Tests-Web' 'OB-Web' 'OmniBrowser' )! ! !PDPackageAnalyzer class methodsFor: 'instance-creation' stamp: 'jf 10/7/2008 10:56'! onPackages: aCollection ^ self basicNew initializeWithPackageInfos: aCollection; yourself! ! !PDPackageAnalyzer class methodsFor: 'instance-creation' stamp: 'lr 1/5/2008 15:45'! onPackagesNamed: aCollection ^ self onPackages: (aCollection collect: [ :each | PackageInfo named: each ])! ! !PDPackageAnalyzer class methodsFor: 'examples' stamp: 'pmm 9/25/2008 09:07'! pierPackageNames ^ #( 'Magritte-Model' 'Magritte-Seaside' 'Magritte-Tests' 'Pier-Model' 'Pier-Seaside' 'Pier-Tests' 'Pier-Blog' 'Pier-Design' 'Pier-Documents' 'Pier-Forms' 'Pier-Randomizer' 'Pier-Security' 'Pier-Squeak-Persistency')! ! !PDPackageAnalyzer class methodsFor: 'examples' stamp: 'jf 10/6/2008 15:59'! seasidePackageNames ^ #( 'Comet-Core' 'Comet-Examples' 'Comet-Squeak-Core' 'RSS-Core' 'RSS-Examples' 'RSS-Squeak-Core' 'RSS-Squeak-Examples' 'RSS-Tests-Core' 'Scriptaculous-Core' 'Scriptaculous-Squeak-Core' 'Scriptaculous-Tests-Core' 'Scriptaculous-Components' 'Scriptaculous-Tests-Components' 'Seaside-Squeak-Continuation' 'Seaside-RenderLoop' 'Seaside-Platform' 'Seaside-Squeak-Platform' 'Seaside-Core' 'Seaside-Squeak-Core' 'Seaside-Session' 'Seaside-Component' 'Seaside-Canvas' 'Seaside-Tests-Functional' 'Seaside-Tests-Platform' 'Seaside-Tests-Core' 'Seaside-Tests-Session' 'Seaside-Tests-Canvas' 'Seaside-Tests-RenderLoop' 'Seaside-Tests-Squeak-Core' 'Seaside-Tests-Squeak-Development' 'Seaside-Tests-Squeak-Functional' 'Seaside-Tests-Squeak-Platform' 'Seaside-Tests-Color' 'Seaside-Development' 'Seaside-Tests-Development' 'Seaside-Squeak-Development' 'Seaside-Examples' 'Seaside-Environment' 'Seaside-InternetExplorer' 'Seaside-Tests-InternetExplorer' 'Seaside-HTML5' 'Seaside-Tests-HTML5' 'Seaside-Email' 'Seaside-Tests-Email' 'Seaside-Squeak-Email' 'Seaside-Squeak-Kom-Core' 'Seaside-Tests-Squeak-Kom' 'Seaside-Adapters-Swazoo' 'Seaside-Squeak-CodeGeneration')! ! !PDPackageAnalyzer class methodsFor: 'examples' stamp: 'lr 1/5/2008 18:29'! seasidePackageNamesWithoutTests ^ self seasidePackageNames reject: [ :each | each includesSubString: 'Test' ]! ! !PDPackageAnalyzer methodsFor: 'actions' stamp: 'jf 10/7/2008 11:18'! analyze self relation addStaticDependencies. self relation markCycles! ! !PDPackageAnalyzer methodsFor: 'configuration' stamp: 'lr 9/13/2008 09:49'! dependencyColorFor: aCollection ^ (aCollection anySatisfy: [ :each | each isCyclic ]) ifFalse: [ 'black' ] ifTrue: [ 'red' ]! ! !PDPackageAnalyzer methodsFor: 'configuration' stamp: 'lr 1/9/2008 11:22'! dependencyLabelFor: aCollection ^ aCollection size! ! !PDPackageAnalyzer methodsFor: 'configuration' stamp: 'lr 1/9/2008 11:30'! dependencyTooltipFor: aCollection ^ String streamContents: [ :stream | aCollection do: [ :each | stream nextPutAll: each reason ] separatedBy: [ stream nextPutAll: ', ' ] ]! ! !PDPackageAnalyzer methodsFor: 'queries' stamp: 'jf 10/7/2008 09:55'! dependentPackagesFor: aPackage ^ aPackage dependencies inject: IdentityDictionary new into: [ :result :each | (each isExternal and: [ each target isIncluded ]) ifTrue: [ (result at: each target ifAbsentPut: [ OrderedCollection new ]) add: each ]. result ]! ! !PDPackageAnalyzer methodsFor: 'accessing' stamp: 'jf 10/7/2008 11:09'! graph "Anser a GraphViz graph. Requires the GraphViz package to be loaded." | graph | graph := GraphViz new. graph beDirected. self relation includedPackages do: [ :package | graph add: package packageName with: { #href -> '#'. #label -> (self packageLabelFor: package). #tooltip -> (self packageTooltipFor: package) } ] displayingProgress: 'Building graph nodes'. self relation includedPackages do: [ :sourcePackage | (self dependentPackagesFor: sourcePackage) keysAndValuesDo: [ :targetPackage :deps | graph add: sourcePackage packageName -> targetPackage packageName with: { #href -> '#'. #color -> (self dependencyColorFor: deps). #label -> (self dependencyLabelFor: deps). #tooltip -> (self dependencyTooltipFor: deps) } ] ]. ^ graph! ! !PDPackageAnalyzer methodsFor: 'initialization' stamp: 'jf 10/7/2008 11:19'! initializeWithPackageInfos: aCollection self initialize. relation := PDPackageRelation onPackages: (aCollection collect: [ :each | PDPackage on: each ]). self analyze! ! !PDPackageAnalyzer methodsFor: 'actions' stamp: 'lr 1/5/2008 15:53'! open self graph openInteractive! ! !PDPackageAnalyzer methodsFor: 'configuration' stamp: 'lr 9/12/2008 19:27'! packageLabelFor: aPackage | result | result := (MCWorkingCopy allManagers detect: [ :each | each packageName = aPackage packageName ] ifNone: [ ^ aPackage packageName ]) ancestry ancestorString. ^ result isEmptyOrNil ifTrue: [ aPackage packageName ] ifFalse: [ result ]! ! !PDPackageAnalyzer methodsFor: 'configuration' stamp: 'jf 10/7/2008 10:02'! packageTooltipFor: aPackage ^ String streamContents: [ :stream | stream nextPutAll: 'Classes: '; print: aPackage info classes size; nextPutAll: ', '. stream nextPutAll: 'Core Methods: '; print: aPackage info coreMethods size; nextPutAll: ', '. stream nextPutAll: 'Extension Methods: '; print: aPackage info extensionMethods size; nextPutAll: ', '. stream nextPutAll: 'Internal Dependencies: '; print: (aPackage dependencies count: [ :each | each isInternal ]); nextPutAll: ', '. stream nextPutAll: 'External Dependencies: '; print: (aPackage dependencies count: [ :each | each isExternal ]) ]! ! !PDPackageAnalyzer methodsFor: 'accessing' stamp: 'jf 10/7/2008 10:35'! relation ^ relation! ! !PDPackageAnalyzer methodsFor: 'actions' stamp: 'lr 1/5/2008 15:53'! save: aString FileStream forceNewFileNamed: aString do: [ :stream | stream nextPutAll: self graph dot ]! ! Object subclass: #PDPackageDependency instanceVariableNames: 'source target cyclic' classVariableNames: '' poolDictionaries: '' category: 'Package-Dependencies'! PDPackageDependency subclass: #PDExtensionDependency instanceVariableNames: 'theClass selector' classVariableNames: '' poolDictionaries: '' category: 'Package-Dependencies'! !PDExtensionDependency methodsFor: 'printing' stamp: 'lr 1/9/2008 11:23'! printReasonOn: aStream aStream nextPutAll: self theClass name; nextPutAll: '>>'; print: self selector! ! !PDExtensionDependency methodsFor: 'accessing' stamp: 'lr 1/9/2008 10:15'! selector ^selector! ! !PDExtensionDependency methodsFor: 'accessing' stamp: 'lr 1/9/2008 10:15'! selector: aSelector selector := aSelector! ! !PDExtensionDependency methodsFor: 'accessing' stamp: 'lr 1/9/2008 10:15'! theClass ^theClass! ! !PDExtensionDependency methodsFor: 'accessing' stamp: 'lr 1/9/2008 10:15'! theClass: aClass theClass := aClass! ! PDPackageDependency subclass: #PDInheritanceDependency instanceVariableNames: 'theClass superclass' classVariableNames: '' poolDictionaries: '' category: 'Package-Dependencies'! !PDInheritanceDependency methodsFor: 'printing' stamp: 'lr 1/9/2008 11:24'! printReasonOn: aStream aStream nextPutAll: self theClass name; nextPutAll: ' inherits from '; nextPutAll: self superclass name! ! !PDInheritanceDependency methodsFor: 'accessing' stamp: 'lr 1/9/2008 10:14'! superclass ^superclass! ! !PDInheritanceDependency methodsFor: 'accessing' stamp: 'lr 1/9/2008 10:15'! superclass: aClass superclass := aClass! ! !PDInheritanceDependency methodsFor: 'accessing' stamp: 'lr 1/9/2008 10:14'! theClass ^theClass! ! !PDInheritanceDependency methodsFor: 'accessing' stamp: 'lr 1/9/2008 10:15'! theClass: aClass theClass := aClass! ! !PDPackageDependency class methodsFor: 'instance-creation' stamp: 'lr 1/9/2008 09:57'! from: aSourcePackage to: aDestinationPackage ^ self new initialzieFrom: aSourcePackage to: aDestinationPackage! ! !PDPackageDependency methodsFor: 'action' stamp: 'lr 9/13/2008 01:00'! beCyclic cyclic := true! ! !PDPackageDependency methodsFor: 'initialization' stamp: 'lr 9/13/2008 01:00'! initialzieFrom: aSourcePackage to: aTargetPackage source := aSourcePackage. target := aTargetPackage. cyclic := false! ! !PDPackageDependency methodsFor: 'testing' stamp: 'lr 9/13/2008 00:59'! isCyclic ^ cyclic! ! !PDPackageDependency methodsFor: 'testing' stamp: 'lr 1/9/2008 10:39'! isExternal ^ self isInternal not! ! !PDPackageDependency methodsFor: 'testing' stamp: 'lr 1/9/2008 10:39'! isInternal ^ self source = self target! ! !PDPackageDependency methodsFor: 'printing' stamp: 'lr 1/9/2008 11:23'! printOn: aStream self printPackageOn: aStream. self printReasonOn: aStream! ! !PDPackageDependency methodsFor: 'printing' stamp: 'lr 1/9/2008 11:23'! printPackageOn: aStream aStream nextPutAll: self source packageName; nextPutAll: ' depends on '; nextPutAll: self target packageName; nextPutAll: ': '! ! !PDPackageDependency methodsFor: 'printing' stamp: 'lr 1/9/2008 11:23'! printReasonOn: aStream! ! !PDPackageDependency methodsFor: 'accessing' stamp: 'lr 1/9/2008 11:24'! reason ^ String streamContents: [ :stream | self printReasonOn: stream ]! ! !PDPackageDependency methodsFor: 'comparing' stamp: 'lr 1/9/2008 10:01'! sameAs: aDependency ^ self source = aDependency source and: [ self target = aDependency target ]! ! !PDPackageDependency methodsFor: 'accessing' stamp: 'lr 1/9/2008 09:59'! source ^ source! ! !PDPackageDependency methodsFor: 'accessing' stamp: 'lr 1/9/2008 09:59'! target ^ target! ! PDPackageDependency subclass: #PDReferenceDependency instanceVariableNames: 'theClass selector reference' classVariableNames: '' poolDictionaries: '' category: 'Package-Dependencies'! !PDReferenceDependency methodsFor: 'printing' stamp: 'lr 1/9/2008 11:24'! printReasonOn: aStream aStream nextPutAll: self theClass name; nextPutAll: '>>'; print: self selector; nextPutAll: ' references '; nextPutAll: self reference name! ! !PDReferenceDependency methodsFor: 'accessing' stamp: 'lr 1/9/2008 10:15'! reference ^reference! ! !PDReferenceDependency methodsFor: 'accessing' stamp: 'lr 1/9/2008 10:15'! reference: aClass reference := aClass! ! !PDReferenceDependency methodsFor: 'accessing' stamp: 'lr 1/9/2008 10:15'! selector ^selector! ! !PDReferenceDependency methodsFor: 'accessing' stamp: 'lr 1/9/2008 10:15'! selector: aSymbol selector := aSymbol! ! !PDReferenceDependency methodsFor: 'accessing' stamp: 'lr 1/9/2008 10:15'! theClass ^theClass! ! !PDReferenceDependency methodsFor: 'accessing' stamp: 'lr 1/9/2008 10:15'! theClass: aClass theClass := aClass! ! Object subclass: #PDPackageRelation instanceVariableNames: 'packages classToPackage' classVariableNames: '' poolDictionaries: '' category: 'Package-Dependencies'! !PDPackageRelation class methodsFor: 'instance creation' stamp: 'jf 10/7/2008 10:43'! new ^ self onPackages: OrderedCollection new! ! !PDPackageRelation class methodsFor: 'instance creation' stamp: 'jf 10/7/2008 10:41'! onPackages: aCollection ^ self basicNew initializeWithPackages: aCollection; yourself! ! !PDPackageRelation methodsFor: 'actions' stamp: 'jf 10/7/2008 11:17'! addStaticDependencies self packages do: [ :each | self addStaticDependencies: each ] displayingProgress: 'Analyzing Packages'! ! !PDPackageRelation methodsFor: 'actions' stamp: 'jf 10/7/2008 11:18'! addStaticDependencies: aPackage aPackage info classes do: [ :class | class superclass ifNotNilDo: [ :superclass | aPackage add: ((PDInheritanceDependency from: aPackage to: (self packageForClass: superclass)) theClass: class; superclass: superclass) ] ]. aPackage info extensionMethods do: [ :method | aPackage add: ((PDExtensionDependency from: aPackage to: (self packageForClass: method actualClass)) theClass: method actualClass; selector: method methodSymbol) ]. aPackage info methods do: [ :method | method compiledMethod literals allButLast do: [ :literal | (literal isVariableBinding and: [ literal value isBehavior and: [ literal key = literal value name ] ]) ifTrue: [ aPackage add: ((PDReferenceDependency from: aPackage to: (self packageForClass: literal value)) theClass: method actualClass; selector: method methodSymbol; reference: literal value) ] ] ]! ! !PDPackageRelation methodsFor: 'testing' stamp: 'jf 10/7/2008 10:31'! hasCycles ^ self includedPackages anySatisfy: [ :each | each hasCyclicDependencies ]! ! !PDPackageRelation methodsFor: 'accessing' stamp: 'jf 10/7/2008 10:32'! includedPackages "Answer the currently analyzed set of packages." ^ self packages select: [ :each | each isIncluded ]! ! !PDPackageRelation methodsFor: 'initialization' stamp: 'jf 10/7/2008 11:20'! initializeWithPackages: aCollection self initialize. packages := aCollection. packages do: [ :each | each beIncluded ]. classToPackage := IdentityDictionary new.! ! !PDPackageRelation methodsFor: 'private' stamp: 'jf 10/7/2008 11:03'! markCycles self includedPackages do: [ :each | self markCycles: each seen: OrderedCollection new ] displayingProgress: 'Finding Cycles'! ! !PDPackageRelation methodsFor: 'private' stamp: 'jf 10/7/2008 11:02'! markCycles: aPackage seen: aCollection | outgoing start edge | outgoing := aPackage dependencies. outgoing := outgoing select: [ :each | each isExternal ]. outgoing := outgoing groupBy: [ :each | each target ] having: [ :each | true ]. outgoing keysAndValuesDo: [ :package :alledges | edge := alledges atRandom. start := aCollection findLast: [ :each | each source = edge target ]. start isZero ifFalse: [ start to: aCollection size do: [ :index | (aCollection at: index) beCyclic ] ] ifTrue: [ aCollection addLast: edge. self markCycles: edge target seen: aCollection. aCollection removeLast ] ]! ! !PDPackageRelation methodsFor: 'accessing' stamp: 'jf 10/7/2008 10:35'! outgoing "Answer a collection of all dependencies that point out of the current package set." ^ self includedPackages inject: Bag new into: [ :outgoing :package | package dependencies do: [ :each | (each isInternal or: [ each target isIncluded ]) ifFalse: [ outgoing add: each ] ] ]! ! !PDPackageRelation methodsFor: 'queries' stamp: 'jf 10/7/2008 11:16'! packageForClass: aClass | info | ^ classToPackage at: aClass ifAbsentPut: [ self packages detect: [ :each | each info includesClass: aClass ] ifNone: [ info := PackageOrganizer default packageOfClass: aClass ifNone: [ nil ]. info isNil ifTrue: [ nil ] ifFalse: [ PDPackage on: info ] ] ]! ! !PDPackageRelation methodsFor: 'accessing' stamp: 'jf 10/7/2008 10:28'! packages "Answer all seen packages." ^ packages! !