Project.st
author Claus Gittinger <cg@exept.de>
Thu, 23 Nov 1995 18:13:04 +0100
changeset 630 b785d23d7c5b
parent 617 427245e28240
child 654 1e039f69fdee
permissions -rw-r--r--
version at the end

"
 COPYRIGHT (c) 1993 by Claus Gittinger
	      All Rights Reserved

 This software is furnished under a license and may be used
 only in accordance with the terms of that license and with the
 inclusion of the above copyright notice.   This software may not
 be provided or otherwise made available to, or used by, any
 other person.  No title to or ownership of the software is
 hereby transferred.
"

Object subclass:#Project
	 instanceVariableNames:'name changeSet views directoryName properties packageName'
	 classVariableNames:'CurrentProject SystemProject NextSequential'
	 poolDictionaries:''
	 category:'System-Support'
!

!Project class methodsFor:'documentation'!

copyright
"
 COPYRIGHT (c) 1993 by Claus Gittinger
	      All Rights Reserved

 This software is furnished under a license and may be used
 only in accordance with the terms of that license and with the
 inclusion of the above copyright notice.   This software may not
 be provided or otherwise made available to, or used by, any
 other person.  No title to or ownership of the software is
 hereby transferred.
"
!

documentation
"
    this class is still under construction (especially the build features are unfinished).
    Currently, all it does is keep track of per-project views 
    (to hide or show them), define the directory when filing-out,
    and define packageName for new classes and methods.

    Future: 
	- keep track of per-project changes
	- allow speficiation of the type of the project (application, library, etc)
	- allow building of whatever the target (as defined by the type) is
	  (this will allow build of class libs and apps by clicking a button)
	- allow removal of project specific classes, methods etc.
"
! !

!Project class methodsFor:'initialization'!

initialize
    SystemProject isNil ifTrue:[
	NextSequential := 1.
	SystemProject := self new name:'default'.
	SystemProject packageName:'private'.
	"
	 the SystemProject does not keep a record if changes,
	 but instead depends on the changes file - recording anything there.
	"
	SystemProject changeSet:nil.
    ].

    CurrentProject := SystemProject.

    "
     Project initialize
    "
! !

!Project class methodsFor:'instance creation'!

new
    ^ self basicNew initialize
! !

!Project class methodsFor:'accessing'!

current
    "return the currently active project"

    ^ CurrentProject

    "
     Project current
    "
!

current:aProject
    "set the currently active project"

    CurrentProject := aProject.
    self changed:#currentProject
!

currentPackageName
    CurrentProject notNil ifTrue:[
	^ CurrentProject packageName
    ].
    ^ 'no package' 

    "
     Project currentPackageName
    "
!

defaultProject
    "return the SystemDefault project"

    ^ SystemProject.
!

setDefaultProject
    "set the currently active project to be the SystemDEfault project"

    self current:SystemProject.
! !

!Project class methodsFor:'changes management'!

addMethodCategoryChange:aMethod category:newCategory in:aClass
    |p c|

    p := CurrentProject.
    (p notNil 
    and:[(c := p changeSet) notNil]) ifTrue:[
	c addMethodCategoryChange:aMethod category:newCategory in:aClass 
    ]
!

addMethodChange:aMethod in:aClass
    "add a change for a method to the current project"

    |p c|

    p := CurrentProject.
    (p notNil 
    and:[(c := p changeSet) notNil]) ifTrue:[
	c addMethodChange:aMethod in:aClass 
    ]
!

addMethodPrivacyChange:aMethod in:aClass
    |p c|

    p := CurrentProject.
    (p notNil 
    and:[(c := p changeSet) notNil]) ifTrue:[
	c addMethodPrivacyChange:aMethod in:aClass 
    ]

    "Modified: 27.8.1995 / 22:48:17 / claus"
!

currentProjectDirectory
    "return the name of the directory to use for fileOut.
     The returned name already includes a file-separator at the end, 
     so the filename can be concatenated to it."

    |p dirName|

    p := CurrentProject.
    p notNil ifTrue:[
	dirName := p directory  
    ] ifFalse:[
	dirName := '.'
    ].
    ^ dirName , Filename separator asString
! !

!Project methodsFor:'accessing'!

changeSet
    ^ changeSet
!

changeSet:aChangeSet
    changeSet := aChangeSet
!

directory
    directoryName isNil ifTrue:[^ '.'].
    ^ directoryName
!

directory:aDirectoryName
    directoryName := aDirectoryName.
    self == CurrentProject ifTrue:[
	Project changed:#directory 
    ]
!

name
    ^ name
!

name:aString
    name := aString.
    self == CurrentProject ifTrue:[
	Project changed:#name
    ]
!

packageName
    ^ packageName
!

packageName:aStringOrSymbol
    packageName := aStringOrSymbol asSymbol.
    self == CurrentProject ifTrue:[
	Project changed:#package
    ]
!

views
    ^ views asArray
!

views:aSetOfViews
    views := WeakIdentitySet withAll:aSetOfViews
! !

!Project methodsFor:'initialization'!

initialize
    |numString|

    views := WeakIdentitySet new.
    numString := NextSequential printString.
    NextSequential := NextSequential + 1.

    name := 'new Project-' , numString.
    packageName := 'private-' , numString.
    "/ changeSet := ChangeSet new.
    self directory:'.'
! !

!Project methodsFor:'maintenance'!

buildProject
    OperatingSystem executeCommand:('cd ' , self directory , ' ; make')
!

createMakefile
    "creates an initial makefile, which will recreate a correct
     Makefile, then compile all"

    |d f out in topName|

    Transcript showCr:'creating Makefile'.

    d := directoryName asFilename.
    f := d construct:'Makefile'.
    f exists ifTrue:[
	f renameTo:(d construct:'Makefile.bak')
    ].
    out := f writeStream.
    out isNil ifTrue:[
	self warn:'cannot create Makefile'.
	^ self
    ].
    in := Smalltalk systemFileStreamFor:'rules/stdHeader'.
    out nextPutAll:in contents asString.
    in close.

    topName := self findTopFrom:directoryName.

    out nextPutAll:'#TOP=/usr/local/lib/smalltalk'; cr.
    out nextPutAll:'TOP=' , topName; cr.
    out nextPutAll:'target:'; cr.
    out tab; nextPutAll:'touch Make.proto'; cr.
    out tab; nextPutAll:'$(MAKE) Makefile'; cr.
    out tab; nextPutAll:'make'; cr; cr.

    in := Smalltalk systemFileStreamFor:'configurations/COMMON/defines'.
    out nextPutAll:in contents asString.
    in close.

    in := Smalltalk systemFileStreamFor:'configurations/vendorConf'.
    out nextPutAll:in contents asString.
    in close.

    in := Smalltalk systemFileStreamFor:'configurations/myConf'.
    out nextPutAll:in contents asString.
    in close.

    in := Smalltalk systemFileStreamFor:'rules/stdRules'.
    out nextPutAll:in contents asString.
    in close.
    out close
!

createProjectFiles
    "actually, creates all files to do a make in the project directory"

    directoryName asFilename exists ifFalse:[
	(self confirm:'create new projectDirectory: ' , directoryName) 
	    ifFalse:[^ self].
	OperatingSystem recursiveCreateDirectory:directoryName.
    ].
    self createMakefile.
    self createSourcefiles.
    self createProtoMakefile.
!

createProtoMakefile
    "creates a Make.proto file"

    |d f s type appName libName startUpClass startUpSelector
     topName classes|

    topName := self findTopFrom:directoryName.

    Transcript showCr:'creating Make.proto'.

    d := directoryName asFilename.
    f := d construct:'Make.proto'.
    f exists ifTrue:[
	f renameTo:(d construct:'Make.proto.bak')
    ].
    s := f writeStream.
    s isNil ifTrue:[
	self warn:'cannot create prototype Makefile'.
	^ self
    ].
    s nextPutAll:'#
# ' , Smalltalk timeStamp , '
#
# created by Smalltalks Project support
#


# the next line defines the path to the TOP directory,
# (where the directories "configurations" and "include" are found)
#
#TOP=/usr/local/lib/smalltalk
TOP=' , topName ,'

#
# add any subdirectories that have to be visited by make
#
SUBDIRS=

#
# do not change
#
SHELL=/bin/sh

'.

    s nextPutAll:'#
# set the stc options
#
STCOPT=$(DEFAULT_STCOPT)
# STCOPT=+optspace2
# STCOPT=+optspace2 -warnNonStandard

#
# and packageName option
#
STCLOCALOPT=''-Pprivate-classes-(libapp)''

'.

    type := #library.
    appName := 'app'.
    libName := 'lib'.
    startUpClass := 'Smalltalk'.
    startUpSelector := 'start'.

    properties notNil ifTrue:[
	type := properties at:#projectType ifAbsent:type.
	appName := properties at:#applicationName ifAbsent:appName.
	startUpClass := properties at:#startupClass ifAbsent:startUpClass.
	startUpSelector := properties at:#startupSelector ifAbsent:startUpSelector.
    ].

    s nextPutAll:'#
# define the name of the library to create
#
'.
    s nextPutAll:'LIBNAME=lib' , appName; cr; cr.

    s nextPutAll:'#
# the target rule:
#
all::   abbrev.stc objs genClassList $(OBJTARGET)

'.

    type == #executable ifTrue:[
	s nextPutAll:'PROGS = ' , appName; cr.
	s nextPutAll:('STARTUP_CLASS=' , startUpClass); cr.
	s nextPutAll:'STARTUP_SELECTOR="' , startUpSelector; nextPutAll:'"'; cr.
    ].

    s nextPutAll:'#
# define the object files that are to be created
#
'.
    s nextPutAll:'OBJS='.

    (classes := self classes) notNil ifTrue:[
	classes do:[:aClass |
	    |abbrev|

	    s nextPutAll:' \'. s cr.
	    abbrev := Smalltalk fileNameForClass:aClass name.
	    s nextPutAll:'  '; nextPutAll:abbrev; nextPutAll:'.o'.
	].
    ].
    s cr; cr.

    s nextPutAll:'#
# dependencies:
#
I=$(TOP)/include
RT_STUFF=$(I)/Object.H $(I)/stc.h $(I)/stcIntern.h

'.
    classes notNil ifTrue:[
	classes do:[:aClass |
	    |abbrev|

	    abbrev := Smalltalk fileNameForClass:aClass name.
	    s nextPutAll:abbrev; nextPutAll:'.o: '.
	    s nextPutAll:abbrev; nextPutAll:'.st '.
	    aClass allSuperclassesDo:[:superClass|
		s nextPutAll:'$(I)/'.
		s nextPutAll:(Smalltalk fileNameForClass:superClass name) , '.H '.
	    ].
	    s nextPutAll:'$(RT_STUFF)';  cr.
	].
    ].
    s cr; cr.

    type == #executable ifTrue:[
	s nextPutAll:'all:: $(PROGS)'; cr.

	s nextPutAll:appName.
	s nextPutAll:':: main.o classList.o $(OBJS)'; cr.
	s tab;      nextPutAll:'$(LD) $(ST_LDFLAG) $(LDFLAGS) -o ';
		    nextPutAll:appName;
		    nextPutAll:' \'; cr.
	s tab; tab; nextPutAll:'$(CRT0) main.$(O) classList.$(O) $(OBJS) $(EXTRA_OBJ) $(LIBOBJS) \'; cr.
	s tab; tab; nextPutAll:'$(LIBRUNDIR)/hidata.o $(LIBRUN) \'; cr.
	s tab; tab; nextPutAll:'$(MATHLIB) $(EXTRALIBS) -lXext $(SYSLIBS) $(OTHERLIBS) $(CRTN)'; cr.
    ].

    s close
!

createSourcefiles
    "creates all Smalltalk-source files in the project directory"

    |classes methods methodClasses dir stream|

    dir := FileDirectory directoryNamed:self directory.
    Transcript showCr:'creating sources in ' , dir pathName , ' ...'; endEntry.

    classes := self classes.
    classes isNil ifTrue:[
	self warn:'no classes in current project'
    ].

    classes notNil ifTrue:[
	classes do:[:aClass |
	    aClass isLoaded ifFalse:[
		aClass autoload.
	    ].
	].
	classes := classes topologicalSort:[:a :b | a isSubclassOf:b].

	classes do:[:aClass |
	    Transcript show:' ... '; showCr:aClass name, '.st'; endEntry.
	    aClass fileOutIn:dir
	]
    ].

    methods := self individualMethods.
    methods notNil ifTrue:[
	methods := methods asIdentitySet.
	"
	 get classes ...
	"
	methodClasses := IdentitySet new.
	methods do:[:m | 
			|mCls|

			mCls := m who at:1.
			mCls isMeta ifTrue:[
			    mCls := mCls soleInstance.
			].
			methodClasses add:mCls].
	"
	 fileOut by class
	"
	methodClasses do:[:cls |
	    stream := (self directory asFilename construct:(cls name , '.chg')) writeStream.

	    Transcript show:' ... '; showCr:cls name, '.chg'; endEntry.
	    methods do:[:m |
		|mCls|

		mCls := m who at:1.
		(mCls == cls or:[mCls == cls class]) ifTrue:[
		    mCls fileOutMethod:m on:stream.
		].
		stream cr.
	    ].
	    stream close.
	].
    ].
!

findTopFrom:directoryName
    "returns the relative path from directoryName to the TOP
     directory."

    |topName relParent foundTop|

    "/ find TOP
    relParent := '..'.
    foundTop := false.
    [foundTop] whileFalse:[
	topName := directoryName , '/' , relParent.
	topName asFilename pathName = '/' ifTrue:[
	    self warn:'could not find TOP; assume absoulte path to TOP'.
	    topName := '/usr/local/lib/smalltalk'.
	    foundTop := true.
	] ifFalse:[
	    (topName , '/configurations') asFilename exists ifTrue:[
		(topName , '/include') asFilename exists ifTrue:[
		    foundTop := true.
		    topName := relParent.
		]
	    ].
	    foundTop ifFalse:[
		relParent := relParent , '/..'.
	    ]        
	]
    ].
    ^ topName
! !

!Project methodsFor:'properties'!

properties
    ^ properties
!

properties:p
    properties := p
!

type
    "return the type of project"

    ^ properties at:#type ifAbsent:[#application]
!

type:aSymbol
    "set the projects type"

    (#(application library smalltalk) includes:aSymbol) ifFalse:[
	self warn:'invalid project type'.
	^ self
    ].
    properties at:#type put:aSymbol
! !

!Project methodsFor:'queries'!

classes
    "return a collection of classes belonging to that project"

    |classes|

    properties notNil ifTrue:[classes := properties at:#classes ifAbsent:nil].
    classes isNil ifTrue:[
	classes := OrderedCollection new.
	Smalltalk allClassesDo:[:aClass | aClass package = packageName ifTrue:[classes add:aClass]].
	classes isEmpty ifTrue:[^ nil].
    ].
    ^ classes

!

individualMethods
    "return a collection of individual methods belonging to that project,
     only methods are returned which are not contained in the
     projects class set."

    |classes methods|

    classes := self classes.
    classes notNil ifTrue:[
	classes := classes asIdentitySet.
    ] ifFalse:[
	classes := #()
    ].

    methods := IdentitySet new.
    Smalltalk allBehaviorsDo:[:cls |
	(classes isNil or:[(classes includes:cls) not]) ifTrue:[
	    cls methodArray do:[:m |
		m package = packageName ifTrue:[
		    methods add:m
		]
	    ].
	    cls class methodArray do:[:m |
		m package = packageName ifTrue:[
		    methods add:m
		]
	    ].
	]
    ].
    ^ methods asArray

    "
     Project current classes
     Project current individualMethods
    "
! !

!Project methodsFor:'specifications'!

readSpec
    |s chunk fileName|

    fileName := (properties at:#directoryName) asFilename construct:'.project'.
    s := fileName readStream.
    s isNil ifTrue:[^ self].
    [s atEnd] whileFalse:[
	chunk := s nextChunk.
	Compiler evaluate:chunk receiver:properties notifying:nil
    ].
    s close.

    "(Project new directory:'../projects/Clock') readSpec"
!

saveSpec
    |f d s|

    d := (properties at:#directoryName) asFilename.
    d exists ifFalse:[
	self error:'directory does not exist'.
	^ self
    ].
    f := d construct:'.project'.
    s := f writeStream.
    s isNil ifTrue:[^ self].
    properties associationsDo:[:aProp |
	(aProp == #directoryName) ifFalse:[
	    s nextChunkPut:('self at:' , aProp key storeString, 
			       ' put:' , aProp value storeString).
	    s cr
	]
    ].
    s close

    "((Project new directory:'../projects/Clock') readSpec
	 directory:'../projects/xxx') saveSpec"
! !

!Project methodsFor:'views'!

addView:aView
    views notNil ifTrue:[views add:aView]
!

destroyViews
    "destroy all views of this project"

    views notNil ifTrue:[
	views do:[:aView |
	    aView notNil ifTrue:[aView destroy]
	]
    ].
!

hideViews
    "hide all views of this project"

    views notNil ifTrue:[
	views do:[:aView |
	    aView notNil ifTrue:[aView unrealize]
	]
    ].
!

removeView:aView
    views notNil ifTrue:[views remove:aView ifAbsent:[]]
!

showViews
    views notNil ifTrue:[
	views do:[:aView |
	    aView notNil ifTrue:[aView rerealize]
	]
    ].
! !

!Project class methodsFor:'documentation'!

version
    ^ '$Header: /cvs/stx/stx/libbasic/Project.st,v 1.30 1995-11-23 17:12:25 cg Exp $'
! !
Project initialize!