| 1 |
unit Workspaces; |
|---|
| 2 |
|
|---|
| 3 |
{ Workspace classes } |
|---|
| 4 |
|
|---|
| 5 |
interface |
|---|
| 6 |
uses classes, Projects, SimpleXML; |
|---|
| 7 |
|
|---|
| 8 |
{ Configuration options for a Workspace } |
|---|
| 9 |
type |
|---|
| 10 |
TWorkspaceCfg = class |
|---|
| 11 |
compiler : string; { location of the dmd compiler } |
|---|
| 12 |
linker : string; { location of the linker } |
|---|
| 13 |
sourceEditor : string; { location of the source editor } |
|---|
| 14 |
editorCommand : string; { command string for invoking source editor } |
|---|
| 15 |
paths : TStringList; |
|---|
| 16 |
|
|---|
| 17 |
constructor Create; |
|---|
| 18 |
destructor Free; |
|---|
| 19 |
|
|---|
| 20 |
procedure CopyTo( other : TWorkspaceCfg ); |
|---|
| 21 |
|
|---|
| 22 |
function toXML : Node; |
|---|
| 23 |
procedure fromXML( xml : Node ); |
|---|
| 24 |
end; |
|---|
| 25 |
|
|---|
| 26 |
|
|---|
| 27 |
{ Main Workspace class } |
|---|
| 28 |
type |
|---|
| 29 |
TWorkspace = class |
|---|
| 30 |
name : string; { onscreen title of the workspace } |
|---|
| 31 |
filename : string; { filename to save workspace to } |
|---|
| 32 |
output : TStringList; { results from compile/build } |
|---|
| 33 |
projects : TList; |
|---|
| 34 |
failed : TStringList; { projects which failed to load } |
|---|
| 35 |
config : TWorkspaceCfg; |
|---|
| 36 |
|
|---|
| 37 |
function Saved : boolean; |
|---|
| 38 |
|
|---|
| 39 |
constructor Create; |
|---|
| 40 |
destructor Free; |
|---|
| 41 |
procedure Save; |
|---|
| 42 |
procedure SaveAs( fname : string ); |
|---|
| 43 |
|
|---|
| 44 |
{ Add a project to this workspace } |
|---|
| 45 |
procedure Add( proj :TProject ); |
|---|
| 46 |
|
|---|
| 47 |
{ Invoke the source editor for the passed file } |
|---|
| 48 |
procedure EditFile( fname : string ; LN:Integer ); |
|---|
| 49 |
|
|---|
| 50 |
{ Invoke the compiler for the passed project and file } |
|---|
| 51 |
function CompileFile( proj:TProject; fname : string ) : boolean; |
|---|
| 52 |
|
|---|
| 53 |
{ Conditionally invoke the compiler using the current FullBuild option } |
|---|
| 54 |
function BuildFile( proj:TProject; fname : string ) : boolean; |
|---|
| 55 |
|
|---|
| 56 |
{ Build all files in the passed project } |
|---|
| 57 |
function BuildAll( p : TProject ) : boolean; |
|---|
| 58 |
|
|---|
| 59 |
{ Build an EXE file for the passed project } |
|---|
| 60 |
function BuildProjectEXE( p : TProject ) : boolean; |
|---|
| 61 |
|
|---|
| 62 |
{ Build a LIB file for the passed project } |
|---|
| 63 |
function BuildProjectLIB( p : TProject ) : boolean; |
|---|
| 64 |
|
|---|
| 65 |
{ Find a project based on passed name } |
|---|
| 66 |
function FindProject( filename : string ) : TProject; |
|---|
| 67 |
|
|---|
| 68 |
protected |
|---|
| 69 |
{ Generates a command line string for editing a source file } |
|---|
| 70 |
function GetEditString( fname:string; LN : Integer ) : string ; |
|---|
| 71 |
end; |
|---|
| 72 |
|
|---|
| 73 |
{ Loads a workspace file, returns nil if could not load } |
|---|
| 74 |
function LoadWorkspace( fname : string ) :TWorkspace ; |
|---|
| 75 |
|
|---|
| 76 |
const |
|---|
| 77 |
FileVersion = '001'; |
|---|
| 78 |
|
|---|
| 79 |
|
|---|
| 80 |
implementation |
|---|
| 81 |
|
|---|
| 82 |
uses UMain, SysUtils, MyUtils, Windows, FileCtrl, Clipbrd; |
|---|
| 83 |
|
|---|
| 84 |
|
|---|
| 85 |
function TWorkspace.GetEditString( fname:string; LN: INteger ) : string ; |
|---|
| 86 |
var |
|---|
| 87 |
line, lcline : string; |
|---|
| 88 |
begin |
|---|
| 89 |
Result := ''; |
|---|
| 90 |
line := Trim(self.config.editorCommand) ; |
|---|
| 91 |
if Empty( line ) then line:='$file /L:$Line'; |
|---|
| 92 |
|
|---|
| 93 |
lcline := LowerCase( line ); |
|---|
| 94 |
if Pos( '$file', lcline )<1 then line := '$file ' + line; |
|---|
| 95 |
|
|---|
| 96 |
line:= StringReplace( line, '$file', fname, [rfReplaceAll,rfIgnoreCase] ); |
|---|
| 97 |
if LN>0 then |
|---|
| 98 |
line:=StringReplace( line, '$line', Format( '%d', [LN] ), [rfReplaceAll,rfIgnoreCase] ) |
|---|
| 99 |
else |
|---|
| 100 |
line:=StringReplace( line, '$line', Format( '%s', [''] ), [rfReplaceAll,rfIgnoreCase] ); |
|---|
| 101 |
Result := line; |
|---|
| 102 |
end; |
|---|
| 103 |
|
|---|
| 104 |
|
|---|
| 105 |
|
|---|
| 106 |
{Invoke Editor} |
|---|
| 107 |
procedure TWorkspace.EditFile( fname : string ; LN : Integer ); |
|---|
| 108 |
var |
|---|
| 109 |
args : TStringList; |
|---|
| 110 |
argstring : string; |
|---|
| 111 |
editor : string; |
|---|
| 112 |
begin |
|---|
| 113 |
editor := Path( config.sourceEditor ); |
|---|
| 114 |
|
|---|
| 115 |
args := TStringList.Create; |
|---|
| 116 |
|
|---|
| 117 |
if Pos( ' ', fname )>0 then fname:= '"' + fname + '"'; |
|---|
| 118 |
|
|---|
| 119 |
if LN>=0 then |
|---|
| 120 |
args.add( GetEditString(fname,LN) ) |
|---|
| 121 |
else |
|---|
| 122 |
args.add( fname ); |
|---|
| 123 |
argstring := Join( args, ' '); |
|---|
| 124 |
args.Free; |
|---|
| 125 |
Clipboard.SetTextBuf( PChar( argstring ) ); |
|---|
| 126 |
Win32Exec( config.sourceEditor, argstring ); |
|---|
| 127 |
end; |
|---|
| 128 |
|
|---|
| 129 |
|
|---|
| 130 |
|
|---|
| 131 |
|
|---|
| 132 |
{ Compiles a source file, true if compilation was successful } |
|---|
| 133 |
function TWorkspace.CompileFile( proj: TProject ; fname : string ) : boolean; |
|---|
| 134 |
var |
|---|
| 135 |
cmd, dest : string; |
|---|
| 136 |
qname : string; |
|---|
| 137 |
begin |
|---|
| 138 |
result := false; |
|---|
| 139 |
output.Clear; |
|---|
| 140 |
proj.sources.ClearErrors( fname ); |
|---|
| 141 |
if not DirectoryExists( proj.basepath ) then Exit; |
|---|
| 142 |
SetCurrentDir( proj.basepath ); |
|---|
| 143 |
if not FileExists(fname) then Exit; |
|---|
| 144 |
|
|---|
| 145 |
qname:= ShortName( fname ); |
|---|
| 146 |
|
|---|
| 147 |
cmd := Format( '-c %s %s', [ qname, proj.CompileOptions ] ); |
|---|
| 148 |
dest := proj.outPath; |
|---|
| 149 |
if not DirectoryExists( dest ) then begin |
|---|
| 150 |
output.Add( 'Output Directory does not exist!'); |
|---|
| 151 |
Exit; |
|---|
| 152 |
end; |
|---|
| 153 |
|
|---|
| 154 |
Sysutils.SetCurrentDir( proj.basepath ); |
|---|
| 155 |
|
|---|
| 156 |
{ copy command line string to clipboard } |
|---|
| 157 |
Clipboard.SetTextBuf( PChar( config.compiler + ' ' + cmd ) ); |
|---|
| 158 |
ConsoleApp( config.compiler, cmd, output ); |
|---|
| 159 |
TrimAll( output ); |
|---|
| 160 |
|
|---|
| 161 |
if output.count > 0 then proj.sources.AddErrors( fname , output ); |
|---|
| 162 |
Result:= ( output.Count = 0 ); |
|---|
| 163 |
Exit; |
|---|
| 164 |
end; |
|---|
| 165 |
|
|---|
| 166 |
|
|---|
| 167 |
function TWorkspace.BuildFile( proj:TProject; fname : string ) : boolean; |
|---|
| 168 |
var |
|---|
| 169 |
outname : string; |
|---|
| 170 |
otime, stime : Integer; { obj time, source time } |
|---|
| 171 |
begin |
|---|
| 172 |
outname := proj.GetResultFile( fname ); |
|---|
| 173 |
proj.sources.ClearErrors( fname ); |
|---|
| 174 |
if (not proj.fullbuild) and FileExists(outname) then |
|---|
| 175 |
begin |
|---|
| 176 |
stime := FileAge( fname ); |
|---|
| 177 |
otime := FileAge( proj.GetResultFile( fname ) ); |
|---|
| 178 |
if otime >= stime then begin |
|---|
| 179 |
result:= true; |
|---|
| 180 |
Exit; |
|---|
| 181 |
end; |
|---|
| 182 |
end; |
|---|
| 183 |
result:= CompileFile( proj, fname ); |
|---|
| 184 |
end; |
|---|
| 185 |
|
|---|
| 186 |
|
|---|
| 187 |
function TWorkspace.BuildAll( p : TProject ) : boolean; |
|---|
| 188 |
var |
|---|
| 189 |
N : Integer; |
|---|
| 190 |
begin |
|---|
| 191 |
Result:= false; |
|---|
| 192 |
with p.sources do |
|---|
| 193 |
for N:= 0 to dfiles.Count-1 do |
|---|
| 194 |
if not BuildFile( p, dfiles[N] ) then Exit; |
|---|
| 195 |
|
|---|
| 196 |
Result:=true; |
|---|
| 197 |
end; |
|---|
| 198 |
|
|---|
| 199 |
|
|---|
| 200 |
function TWorkspace.BuildProjectEXE( p : TProject ) : boolean; |
|---|
| 201 |
var |
|---|
| 202 |
outfiles : TStringList; |
|---|
| 203 |
linkcmd : string; |
|---|
| 204 |
begin |
|---|
| 205 |
Result:=FileExists( self.config.compiler); |
|---|
| 206 |
if not Result then Exit; |
|---|
| 207 |
|
|---|
| 208 |
output.Clear; |
|---|
| 209 |
Result := Self.BuildAll( p ); |
|---|
| 210 |
if not Result then Exit; |
|---|
| 211 |
|
|---|
| 212 |
outfiles := TStringList.Create; |
|---|
| 213 |
outfiles.Duplicates := dupIgnore; |
|---|
| 214 |
p.GetObjectFiles( outfiles ); |
|---|
| 215 |
outfiles.AddStrings( p.sources.objfiles ); |
|---|
| 216 |
outfiles.AddStrings( p.sources.libfiles ); |
|---|
| 217 |
|
|---|
| 218 |
MyUtils.GetShortNames( outfiles ); |
|---|
| 219 |
|
|---|
| 220 |
linkcmd := Format( '%s %s', |
|---|
| 221 |
[ p.LinkOptions, Join( outfiles, ' ') ] ); |
|---|
| 222 |
ClipBoard.SetTextBuf( PChar( linkcmd ) ); |
|---|
| 223 |
SetCurrentDir( p.basepath ); |
|---|
| 224 |
ConsoleApp( config.compiler, linkcmd, output ) ; |
|---|
| 225 |
result:= true; |
|---|
| 226 |
end; |
|---|
| 227 |
|
|---|
| 228 |
|
|---|
| 229 |
|
|---|
| 230 |
function TWorkspace.BuildProjectLIB( p : TProject ) : boolean; |
|---|
| 231 |
var |
|---|
| 232 |
outfiles : TStringList; |
|---|
| 233 |
libcmd : string; |
|---|
| 234 |
libfile : string; |
|---|
| 235 |
N : Integer; |
|---|
| 236 |
begin |
|---|
| 237 |
Result:=FileExists( self.config.compiler); |
|---|
| 238 |
if not Result then Exit; |
|---|
| 239 |
|
|---|
| 240 |
output.Clear; |
|---|
| 241 |
Result := Self.BuildAll( p ); |
|---|
| 242 |
if not Result then Exit; |
|---|
| 243 |
|
|---|
| 244 |
outfiles := TStringList.Create; |
|---|
| 245 |
p.GetObjectFiles( outfiles ); |
|---|
| 246 |
outfiles.AddStrings( p.sources.objfiles ); |
|---|
| 247 |
outfiles.AddStrings( p.sources.libfiles ); |
|---|
| 248 |
|
|---|
| 249 |
GetShortNames( outfiles ); |
|---|
| 250 |
libfile := p.LibName; |
|---|
| 251 |
|
|---|
| 252 |
N := outfiles.IndexOf( libfile ); |
|---|
| 253 |
if N>=0 then outfiles.Delete( N ); |
|---|
| 254 |
|
|---|
| 255 |
libfile := ShortName( libfile ); |
|---|
| 256 |
|
|---|
| 257 |
SetCurrentDir( p.basepath ); |
|---|
| 258 |
|
|---|
| 259 |
libcmd := Format( '-c %s %s', [ libfile, Join( outfiles, ' ' ) ] ); |
|---|
| 260 |
ClipBoard.SetTextBuf( PChar( libcmd ) ); |
|---|
| 261 |
ConsoleApp( 'lib.exe', libcmd, output ) ; |
|---|
| 262 |
TrimAll( output ); |
|---|
| 263 |
outfiles.Free; |
|---|
| 264 |
result:= true; |
|---|
| 265 |
end; |
|---|
| 266 |
|
|---|
| 267 |
|
|---|
| 268 |
|
|---|
| 269 |
constructor TWorkspace.Create; |
|---|
| 270 |
begin |
|---|
| 271 |
filename :=''; |
|---|
| 272 |
name := ''; |
|---|
| 273 |
output := TStringList.Create; |
|---|
| 274 |
projects := TList.Create; |
|---|
| 275 |
failed := TStringList.Create; |
|---|
| 276 |
config := TWorkspaceCfg.Create; |
|---|
| 277 |
end; |
|---|
| 278 |
|
|---|
| 279 |
|
|---|
| 280 |
destructor TWorkspace.Free; |
|---|
| 281 |
var |
|---|
| 282 |
N : LongInt; |
|---|
| 283 |
proj : TProject; |
|---|
| 284 |
begin |
|---|
| 285 |
for N:= 0 to projects.Count-1 do begin |
|---|
| 286 |
proj := projects[N]; |
|---|
| 287 |
proj.Free; |
|---|
| 288 |
end; |
|---|
| 289 |
output.Free; |
|---|
| 290 |
projects.Free; |
|---|
| 291 |
failed.Free; |
|---|
| 292 |
config.Free; |
|---|
| 293 |
end; |
|---|
| 294 |
|
|---|
| 295 |
|
|---|
| 296 |
function TWorkspace.Saved : boolean; |
|---|
| 297 |
begin |
|---|
| 298 |
Result:= ( not Empty(filename) ) and FileExists( filename ); |
|---|
| 299 |
end; |
|---|
| 300 |
|
|---|
| 301 |
procedure TWorkspace.Add( proj: TProject ); |
|---|
| 302 |
begin |
|---|
| 303 |
if not Assigned( proj ) then Exit; |
|---|
| 304 |
proj.parent := Self ; |
|---|
| 305 |
projects.Add( proj ); |
|---|
| 306 |
end; |
|---|
| 307 |
|
|---|
| 308 |
|
|---|
| 309 |
function LoadWorkspace( fname : string ) : TWorkspace; |
|---|
| 310 |
var |
|---|
| 311 |
xml, tmp, cfg : Node; |
|---|
| 312 |
N : LongInt; |
|---|
| 313 |
proj : TProject; |
|---|
| 314 |
str : string; |
|---|
| 315 |
begin |
|---|
| 316 |
Result := nil; |
|---|
| 317 |
|
|---|
| 318 |
if not FileExists( fname ) then exit; |
|---|
| 319 |
|
|---|
| 320 |
xml := LoadXML( fname ); |
|---|
| 321 |
if not Assigned(xml) then Exit; |
|---|
| 322 |
|
|---|
| 323 |
result := TWorkspace.Create ; |
|---|
| 324 |
result.filename := fname; |
|---|
| 325 |
|
|---|
| 326 |
result.name := xml.FindText( 'Name' ); |
|---|
| 327 |
|
|---|
| 328 |
cfg := xml.FindNode( 'WorkspaceConfig' ); |
|---|
| 329 |
result.config.fromXML( cfg ); |
|---|
| 330 |
|
|---|
| 331 |
tmp := xml.FindNode( 'Projects' ); |
|---|
| 332 |
for N:=0 to tmp.Count-1 do begin |
|---|
| 333 |
str := tmp[N].data; |
|---|
| 334 |
proj := LoadProject( str ); |
|---|
| 335 |
if Assigned( proj ) then result.Add( proj ) |
|---|
| 336 |
else result.failed.Add( str ); |
|---|
| 337 |
end; |
|---|
| 338 |
|
|---|
| 339 |
xml.Free; |
|---|
| 340 |
end; |
|---|
| 341 |
|
|---|
| 342 |
procedure TWorkspace.Save; |
|---|
| 343 |
begin |
|---|
| 344 |
if not Empty( self.filename ) then Self.SaveAs( self.filename ); |
|---|
| 345 |
end; |
|---|
| 346 |
|
|---|
| 347 |
procedure TWorkspace.SaveAs( fname : string ); |
|---|
| 348 |
var |
|---|
| 349 |
xml, prj, tmp : Node; |
|---|
| 350 |
I : LongInt; |
|---|
| 351 |
proj : TProject; |
|---|
| 352 |
begin |
|---|
| 353 |
filename := fname; |
|---|
| 354 |
|
|---|
| 355 |
xml:=Node.Create( 'Workspace' ); |
|---|
| 356 |
tmp:=xml.Add( 'Name' ); |
|---|
| 357 |
tmp.data := self.name; |
|---|
| 358 |
|
|---|
| 359 |
xml.AddChild( config.toXML ); |
|---|
| 360 |
prj := xml.Add( 'Projects' ); |
|---|
| 361 |
|
|---|
| 362 |
for I:= 0 to projects.Count-1 do begin |
|---|
| 363 |
proj := projects[I]; |
|---|
| 364 |
tmp := prj.Add( 'Filename' ); |
|---|
| 365 |
tmp.data := proj.filename; |
|---|
| 366 |
end; |
|---|
| 367 |
|
|---|
| 368 |
SaveXML( xml, filename ); |
|---|
| 369 |
xml.Free; |
|---|
| 370 |
end; |
|---|
| 371 |
|
|---|
| 372 |
|
|---|
| 373 |
function TWorkspace.FindProject( filename : string ) : TProject; |
|---|
| 374 |
var |
|---|
| 375 |
proj : TProject; |
|---|
| 376 |
pname : string; |
|---|
| 377 |
N : integer; |
|---|
| 378 |
begin |
|---|
| 379 |
Result := nil; |
|---|
| 380 |
pname := Trim( ExtractFileName( filename )); |
|---|
| 381 |
pname := ChangeFileExt( pname, '' ); |
|---|
| 382 |
for N:=0 to projects.Count-1 do begin |
|---|
| 383 |
proj := projects[ N ]; |
|---|
| 384 |
if proj.filename = filename then |
|---|
| 385 |
begin |
|---|
| 386 |
Result:= proj; |
|---|
| 387 |
Exit; |
|---|
| 388 |
end; |
|---|
| 389 |
end; |
|---|
| 390 |
end; |
|---|
| 391 |
|
|---|
| 392 |
|
|---|
| 393 |
constructor TWorkspaceCfg.Create; |
|---|
| 394 |
begin |
|---|
| 395 |
compiler := ''; |
|---|
| 396 |
sourceEditor := ''; |
|---|
| 397 |
editorCommand := ''; |
|---|
| 398 |
paths := TStringList.Create; |
|---|
| 399 |
end; |
|---|
| 400 |
|
|---|
| 401 |
|
|---|
| 402 |
destructor TWorkspaceCfg.Free; |
|---|
| 403 |
begin |
|---|
| 404 |
paths.Free; |
|---|
| 405 |
end; |
|---|
| 406 |
|
|---|
| 407 |
function TWorkspaceCfg.toXML : Node; |
|---|
| 408 |
var |
|---|
| 409 |
cfg, tmp : Node; |
|---|
| 410 |
begin |
|---|
| 411 |
cfg:= Node.Create( 'WorkspaceConfig' ); |
|---|
| 412 |
tmp := cfg.Add( 'Compiler' ); |
|---|
| 413 |
tmp.data := self.compiler; |
|---|
| 414 |
|
|---|
| 415 |
tmp := cfg.Add( 'Linker' ); |
|---|
| 416 |
tmp.data := self.linker; |
|---|
| 417 |
|
|---|
| 418 |
tmp := cfg.Add( 'Editor' ); |
|---|
| 419 |
tmp.data := self.sourceEditor; |
|---|
| 420 |
|
|---|
| 421 |
tmp := cfg.Add( 'EditCommand' ); |
|---|
| 422 |
tmp.data := self.editorCommand; |
|---|
| 423 |
result:= cfg; |
|---|
| 424 |
end; |
|---|
| 425 |
|
|---|
| 426 |
|
|---|
| 427 |
procedure TWorkspaceCfg.fromXML( xml : Node ); |
|---|
| 428 |
begin |
|---|
| 429 |
if not Assigned(xml) then Exit; |
|---|
| 430 |
self.Compiler := xml.FindText( 'Compiler' ); |
|---|
| 431 |
self.linker := xml.FindText( 'Linker' ); |
|---|
| 432 |
self.sourceEditor := xml.FindText( 'Editor' ); |
|---|
| 433 |
self.editorCommand := xml.FindText( 'EditCommand' ); |
|---|
| 434 |
end; |
|---|
| 435 |
|
|---|
| 436 |
|
|---|
| 437 |
|
|---|
| 438 |
procedure TWorkspaceCfg.CopyTo( other : TWorkspaceCfg ); |
|---|
| 439 |
begin |
|---|
| 440 |
other.compiler := compiler; |
|---|
| 441 |
other.linker := linker; |
|---|
| 442 |
other.sourceEditor := sourceEditor; |
|---|
| 443 |
other.editorCommand := editorCommand; |
|---|
| 444 |
other.paths.Clear; |
|---|
| 445 |
other.paths.AddStrings( paths ); |
|---|
| 446 |
end; |
|---|
| 447 |
|
|---|
| 448 |
{ |
|---|
| 449 |
procedure TWorkspaceCfg.Save( writer:TDataWriter ); |
|---|
| 450 |
begin |
|---|
| 451 |
writer.WriteString( compiler ); |
|---|
| 452 |
writer.WriteString( linker ); |
|---|
| 453 |
writer.WriteString( sourceEditor ); |
|---|
| 454 |
writer.WriteString( editorCommand ); |
|---|
| 455 |
writer.WriteStrings( paths ); |
|---|
| 456 |
end; |
|---|
| 457 |
|
|---|
| 458 |
|
|---|
| 459 |
procedure TWorkspaceCfg.Load( reader : TDataReader ); |
|---|
| 460 |
begin |
|---|
| 461 |
reader.ReadString( compiler ); |
|---|
| 462 |
reader.ReadString( linker ); |
|---|
| 463 |
reader.ReadString( sourceEditor ); |
|---|
| 464 |
reader.ReadString( editorCommand ); |
|---|
| 465 |
reader.ReadStrings( paths ); |
|---|
| 466 |
end; |
|---|
| 467 |
} |
|---|
| 468 |
|
|---|
| 469 |
end. |
|---|