source: tags/1.3.0/Target.pas

Last change on this file was 165, checked in by chronos, 3 months ago

Merged revision(s) 164 from trunk:

  • Fixed: Put compiled files into application data directory if source is in read only directory.
File size: 16.5 KB
Line 
1unit Target;
2
3interface
4
5uses
6 Classes, SysUtils, StrUtils, Registry, RegistryEx, fgl, Dialogs,
7 FileUtil, Process, Menus, LazFileUtils;
8
9type
10 TCompilerOptimization = (coNone, coNormal);
11
12 TTargetCapability = (tcRun, tcPause, tcStop, tcStepOut, tcStepOver, tcStepInto,
13 tcRunToCursor, tcCompile, tcBreakpoint);
14 TTargetCapabilities = set of TTargetCapability;
15
16 TRunState = (rsStopped, rsPaused, rsRunning, rsCompiling);
17
18 TStepOperation = (soNormal, soStepIn, soStepOut);
19
20 TDebugStep = class
21 SourcePosition: Integer;
22 ProgramPosition: Integer;
23 TargetPosition: Integer;
24 Operation: TStepOperation;
25 end;
26
27 { TDebugSteps }
28
29 TDebugSteps = class(TFPGObjectList<TDebugStep>)
30 function SearchBySourcePos(Pos: Integer): TDebugStep;
31 function SearchByProgramPos(Pos: Integer): TDebugStep;
32 function SearchByTargetPos(Pos: Integer): TDebugStep;
33 function SearchIndexByProgramPos(Pos: Integer): Integer;
34 procedure AddStep(SourcePos, TargetPos: Integer; Operation: TStepOperation);
35 procedure UpdateTargetPos(OldProgramFrom, OldProgramTo, NewProgramFrom,
36 NewProgramTo: Integer; NewTarget: Integer = 0);
37 end;
38
39 TBreakPoint = class
40 TargetAddress: Integer;
41 System: Boolean;
42 end;
43
44 { TBreakPoints }
45
46 TBreakPoints = class(TFPGObjectList<TBreakPoint>)
47 procedure AddItem(TargetAddress: Integer);
48 procedure SetSystem(TargetAddress: Integer);
49 procedure AddSystem(TargetAddress: Integer);
50 procedure ClearSystem;
51 function SearchByTargetPos(Pos: Integer): TBreakPoint;
52 end;
53
54 TMessage = class
55 Text: string;
56 Position: TPoint;
57 end;
58
59 { TMessages }
60
61 TMessages = class(TFPGObjectList<TMessage>)
62 private
63 FOnChange: TNotifyEvent;
64 procedure DoChange;
65 public
66 procedure AddMessage(Text: string);
67 procedure AppendMessage(Text: string);
68 property OnChange: TNotifyEvent read FOnChange write FOnChange;
69 end;
70
71 TLogEvent = procedure (Lines: TStrings) of object;
72 TMessageEvent = procedure (Text: string) of object;
73
74 { TTarget }
75
76 TTarget = class
77 private
78 FOnLog: TLogEvent;
79 FOnMessage: TMessageEvent;
80 protected
81 FCompiledExtension: string;
82 FSourceExtension: string;
83 FName: string;
84 FImageIndex: Integer;
85 FCapabilities: TTargetCapabilities;
86 FCompiled: Boolean;
87 function SourceReadNext: Char;
88 protected
89 FSourceCode: string;
90 FTargetCode: string;
91 FTargetIndex: Integer;
92 Indent: Integer;
93 FState: TRunState;
94 FOnChangeState: TNotifyEvent;
95 procedure LoadProgram; virtual;
96 procedure SetSourceCode(AValue: string); virtual;
97 function GetTargetCode: string; virtual;
98 procedure AddLine(Text: string = '');
99 function LongFileName(FileName: string): string;
100 function GetExecutionPosition: Integer; virtual;
101 procedure SetState(AValue: TRunState); virtual;
102 public
103 ProgramName: string;
104 OptimizationLevel: TCompilerOptimization;
105 CompilerPath: string;
106 ExecutorPath: string;
107 ProjectFileName: string;
108 BreakPoints: TBreakPoints;
109 DebugSteps: TDebugSteps;
110 DebugEnabled: Boolean;
111 Selected: Boolean;
112 procedure SendMessage(Text: string);
113 constructor Create; virtual;
114 destructor Destroy; override;
115 procedure Reset; virtual;
116 procedure OptimizeSource; virtual;
117 procedure Compile; virtual;
118 procedure CompileToFile; virtual;
119 function GetSourceFileName: string; virtual;
120 function GetCompileParams: TStringArray; virtual;
121 function GetCompileFileName: string; virtual;
122 procedure RunFromFile; virtual;
123 function GetRunParams: TStringArray; virtual;
124 procedure Run; virtual;
125 procedure Pause; virtual;
126 procedure Stop; virtual;
127 procedure StepOver; virtual;
128 procedure StepInto; virtual;
129 procedure StepOut; virtual;
130 procedure RunToCursor(Pos: Integer); virtual;
131 procedure LoadFromRegistry(Context: TRegistryContext); virtual;
132 procedure SaveToRegistry(Context: TRegistryContext); virtual;
133 property SourceExtension: string read FSourceExtension;
134 property CompiledExtension: string read FCompiledExtension;
135 property Name: string read FName;
136 property ImageIndex: Integer read FImageIndex;
137 property State: TRunState read FState write SetState;
138 property SourceCode: string write SetSourceCode;
139 property TargetCode: string read GetTargetCode;
140 property Compiled: Boolean read FCompiled write FCompiled;
141 property ExecutionPosition: Integer read GetExecutionPosition;
142 property Capabilities: TTargetCapabilities read FCapabilities;
143 property OnChangeState: TNotifyEvent read FOnChangeState write FOnChangeState;
144 property OnLog: TLogEvent read FOnLog write FOnLog;
145 property OnMessage: TMessageEvent read FOnMessage write FOnMessage;
146 end;
147
148 { TTargets }
149
150 TTargets = class(TFPGObjectList<TTarget>)
151 procedure LoadFromRegistry(Context: TRegistryContext);
152 procedure SaveToRegistry(Context: TRegistryContext);
153 function FindByName(Name: string): TTarget;
154 procedure LoadToMenuItem(MenuItem: TMenuItem; Action: TNotifyEvent;
155 CurrentTarget: TTarget);
156 end;
157
158resourcestring
159 SCompilerNotFound = 'Compiler "%s" not found';
160 SExecutorNotFound = 'Executor "%s" not found';
161 SCompiledFileNotFound = 'Program compiled file "%s" not found';
162 SStopped = 'Stopped';
163 SPaused = 'Paused';
164 SRunning = 'Running';
165 SCompiling = 'Compiling';
166 SNone = 'None';
167 SNormal = 'Normal';
168 SProgramIndexError = 'Old program index higher than new (old: %d, new: %d)';
169 SFirstIndexHigherThanLastIndex = 'First index higher than last index';
170 SFirstIndexOutOfRange = 'First index out of range';
171 SLastIndexOutOfRange = 'Last index out of range';
172
173const
174 CompiledDir = 'compiled';
175
176var
177 RunStateText: array[TRunState] of string = (SStopped, SPaused, SRunning,
178 SCompiling);
179 CompilerOptimizationText: array[TCompilerOptimization] of string = (SNone, SNormal);
180
181procedure UpdateTranslation;
182
183
184implementation
185
186uses
187 FormConsole, Common;
188
189procedure UpdateTranslation;
190begin
191 RunStateText[rsStopped] := SStopped;
192 RunStateText[rsPaused] := SPaused;
193 RunStateText[rsRunning] := SRunning;
194 RunStateText[rsCompiling] := SCompiling;
195 CompilerOptimizationText[coNone] := SNone;
196 CompilerOptimizationText[coNormal] := SNormal;
197end;
198
199{ TMessages }
200
201procedure TMessages.DoChange;
202begin
203 if Assigned(FOnChange) then FOnChange(Self);
204end;
205
206procedure TMessages.AddMessage(Text: string);
207var
208 NewItem: TMessage;
209begin
210 NewItem := TMessage.Create;
211 NewItem.Text := Text;
212 Add(NewItem);
213 DoChange;
214end;
215
216procedure TMessages.AppendMessage(Text: string);
217begin
218 if Count > 0 then begin
219 Last.Text := Last.Text + Text;
220 DoChange;
221 end else AddMessage(Text);
222end;
223
224{ TBreakPoints }
225
226procedure TBreakPoints.AddItem(TargetAddress: Integer);
227var
228 NewItem: TBreakPoint;
229begin
230 NewItem := TBreakPoint.Create;
231 NewItem.TargetAddress := TargetAddress;
232 Add(NewItem);
233end;
234
235procedure TBreakPoints.SetSystem(TargetAddress: Integer);
236begin
237 ClearSystem;
238 AddSystem(TargetAddress);
239end;
240
241procedure TBreakPoints.AddSystem(TargetAddress: Integer);
242var
243 NewItem: TBreakPoint;
244begin
245 NewItem := TBreakPoint.Create;
246 NewItem.TargetAddress := TargetAddress;
247 NewItem.System := True;
248 Add(NewItem);
249end;
250
251procedure TBreakPoints.ClearSystem;
252var
253 I: Integer;
254begin
255 for I := Count - 1 downto 0 do
256 if Items[I].System then Delete(I);
257end;
258
259function TBreakPoints.SearchByTargetPos(Pos: Integer): TBreakPoint;
260var
261 I: Integer;
262begin
263 I := 0;
264 while (I < Count) and (Items[I].TargetAddress < Pos) do Inc(I);
265 if I < Count then Result := Items[I]
266 else Result := nil;
267end;
268
269{ TDebugSteps }
270
271function TDebugSteps.SearchBySourcePos(Pos: Integer
272 ): TDebugStep;
273var
274 I: Integer;
275begin
276 I := 0;
277 while (I < Count) and (Items[I].SourcePosition < Pos) do Inc(I);
278 if I < Count then Result := Items[I]
279 else Result := nil;
280end;
281
282function TDebugSteps.SearchByProgramPos(Pos: Integer): TDebugStep;
283var
284 I: Integer;
285begin
286 I := 0;
287 while (I < Count) and (Items[I].ProgramPosition < Pos) do Inc(I);
288 if I < Count then Result := Items[I]
289 else Result := nil;
290end;
291
292function TDebugSteps.SearchByTargetPos(Pos: Integer
293 ): TDebugStep;
294var
295 I: Integer;
296begin
297 I := 0;
298 while (I < Count) and (Items[I].TargetPosition < Pos) do Inc(I);
299 if I < Count then Result := Items[I]
300 else Result := nil;
301end;
302
303function TDebugSteps.SearchIndexByProgramPos(Pos: Integer): Integer;
304var
305 I: Integer;
306begin
307 I := 0;
308 while (I < Count) and (Items[I].ProgramPosition < Pos) do Inc(I);
309 if I < Count then Result := I
310 else Result := -1;
311end;
312
313procedure TDebugSteps.AddStep(SourcePos, TargetPos: Integer;
314 Operation: TStepOperation);
315var
316 NewItem: TDebugStep;
317begin
318 NewItem := TDebugStep.Create;
319 NewItem.SourcePosition := SourcePos;
320 NewItem.ProgramPosition := TargetPos;
321 NewItem.TargetPosition := TargetPos;
322 NewItem.Operation := Operation;
323 Add(NewItem);
324end;
325
326procedure TDebugSteps.UpdateTargetPos(OldProgramFrom, OldProgramTo, NewProgramFrom,
327 NewProgramTo: Integer; NewTarget: Integer = 0);
328var
329 I: Integer;
330 First: Integer;
331 Last: Integer;
332begin
333 First := SearchIndexByProgramPos(OldProgramFrom);
334 Last := SearchIndexByProgramPos(OldProgramTo);
335 if (First <> -1) and (Last <> -1) then begin
336 if First > Last then
337 raise Exception.Create(SFirstIndexHigherThanLastIndex);
338 if (First < 0) or (First >= Count) then
339 raise Exception.Create(SFirstIndexOutOfRange);
340 if (Last < 0) or (Last >= Count) then
341 raise Exception.Create(SLastIndexOutOfRange);
342 for I := Last downto First + 1 do Delete(I);
343
344 if NewProgramTo - NewProgramFrom = 0 then begin
345 Delete(First);
346 end else
347 if NewProgramTo > NewProgramFrom then begin
348 Items[First].ProgramPosition := NewProgramFrom;
349 Items[First].TargetPosition := NewTarget;
350 end else
351 raise Exception.Create(Format(SProgramIndexError, [SProgramIndexError, NewProgramTo]));
352 end;
353end;
354
355{ TTargets }
356
357procedure TTargets.LoadFromRegistry(Context: TRegistryContext);
358var
359 I: Integer;
360begin
361 with TRegistryEx.Create do
362 try
363 CurrentContext := Context;
364 for I := 0 to Count - 1 do
365 Items[I].LoadFromRegistry(TRegistryContext.Create(Context.RootKey, Context.Key + '\' + TTarget(Items[I]).Name));
366 finally
367 Free;
368 end;
369end;
370
371procedure TTargets.SaveToRegistry(Context: TRegistryContext);
372var
373 I: Integer;
374begin
375 with TRegistryEx.Create do
376 try
377 CurrentContext := Context;
378 for I := 0 to Count - 1 do
379 with Items[I] do
380 Items[I].SaveToRegistry(TRegistryContext.Create(Context.RootKey, Context.Key + '\' + TTarget(Items[I]).Name));
381 finally
382 Free;
383 end;
384end;
385
386function TTargets.FindByName(Name: string): TTarget;
387var
388 I: Integer;
389begin
390 I := 0;
391 while (I < Count) and (Items[I].Name <> Name) do Inc(I);
392 if I < Count then Result := Items[I]
393 else Result := nil;
394end;
395
396procedure TTargets.LoadToMenuItem(MenuItem: TMenuItem; Action: TNotifyEvent
397 ; CurrentTarget: TTarget);
398var
399 NewMenuItem: TMenuItem;
400 I: Integer;
401begin
402 if Assigned(MenuItem) then begin
403 while MenuItem.Count > Count do
404 MenuItem.Delete(MenuItem.Count - 1);
405 while MenuItem.Count < Count do begin
406 NewMenuItem := TMenuItem.Create(MenuItem);
407 MenuItem.Add(NewMenuItem);
408 end;
409 for I := 0 to Count - 1 do begin
410 MenuItem.Items[I].Caption := Items[I].Name;
411 MenuItem.Items[I].OnClick := Action;
412 MenuItem.Items[I].ImageIndex := Items[I].ImageIndex;
413 if Items[I] = CurrentTarget then MenuItem.Items[I].Checked := True;
414 end;
415 end;
416end;
417
418{ TTarget }
419
420function TTarget.GetTargetCode: string;
421begin
422 Result := FTargetCode;
423end;
424
425function TTarget.GetExecutionPosition: Integer;
426begin
427 Result := 0;
428end;
429
430procedure TTarget.SetState(AValue: TRunState);
431begin
432 if FState = AValue then Exit;
433 FState := AValue;
434end;
435
436procedure TTarget.SendMessage(Text: string);
437begin
438 if Assigned(FOnMessage) then
439 FOnMessage(Text);
440end;
441
442procedure TTarget.SetSourceCode(AValue: string);
443begin
444 FSourceCode := AValue;
445end;
446
447procedure TTarget.AddLine(Text: string = '');
448begin
449 FTargetCode := FTargetCode + DupeString(' ', Indent) + Text + LineEnding;
450end;
451
452function TTarget.LongFileName(FileName: string): string;
453begin
454 Result := FileName;
455 {$IFDEF WINDOWS}
456 Result := '"' + FileName + '"';
457 {$ENDIF}
458 {$IFDEF UNIX}
459 Result := StringReplace(FileName, ' ', '\ ', [rfReplaceAll]);
460 {$ENDIF}
461end;
462
463constructor TTarget.Create;
464begin
465 inherited;
466 OptimizationLevel := coNormal;
467 BreakPoints := TBreakPoints.Create;
468 DebugSteps := TDebugSteps.Create;
469end;
470
471destructor TTarget.Destroy;
472begin
473 FreeAndNil(DebugSteps);
474 FreeAndNil(BreakPoints);
475 inherited;
476end;
477
478procedure TTarget.Reset;
479begin
480end;
481
482procedure TTarget.OptimizeSource;
483begin
484end;
485
486procedure TTarget.Compile;
487begin
488 LoadProgram;
489 if OptimizationLevel = coNormal then OptimizeSource;
490 Compiled := True;
491end;
492
493procedure TTarget.CompileToFile;
494var
495 SourceFile: string;
496 Lines: TStringList;
497 Output, Error: string;
498 Executable: string;
499 Parameters: array of string;
500begin
501 SourceFile := GetSourceFileName;
502 ForceDirectories(ExtractFilePath(SourceFile));
503 with TStringList.Create do
504 try
505 Text := FTargetCode;
506 SaveToFile(SourceFile);
507 finally
508 Free;
509 end;
510 if CompilerPath <> '' then begin;
511 if FileExists(CompilerPath) then begin
512 Executable := LongFileName(CompilerPath);
513 Parameters := GetCompileParams;
514
515 ExecuteProgramOutput(Executable, GetCompileParams, GetEnvironmentVariables,
516 Output, Error, ExitCode, ExtractFilePath(SourceFile));
517
518 if Assigned(FOnLog) then begin
519 Lines := TStringList.Create;
520 Lines.Text := Output;
521 Lines.Insert(0, Executable + ' ' + Implode(',', Parameters));
522 FOnLog(Lines);
523 Lines.Free;
524 end;
525
526 end else raise Exception.Create(Format(SCompilerNotFound, [CompilerPath]));
527 end;
528end;
529
530function TTarget.GetSourceFileName: string;
531begin
532 Result := ExtractFilePath(ProjectFileName) +
533 CompiledDir + DirectorySeparator + Name + DirectorySeparator +
534 ExtractFileNameOnly(ProjectFileName) + SourceExtension;
535 if not DirectoryIsWritable(ExtractFilePath(Result)) then
536 Result := GetAppConfigDir(False) + CompiledDir + DirectorySeparator + Name +
537 DirectorySeparator + ExtractFileNameOnly(ProjectFileName) + SourceExtension;;
538end;
539
540function TTarget.GetCompileParams: TStringArray;
541begin
542 Result := [LongFileName(GetSourceFileName)];
543end;
544
545function TTarget.GetCompileFileName: string;
546begin
547 Result := ExtractFilePath(ProjectFileName) +
548 CompiledDir + DirectorySeparator + Name + DirectorySeparator +
549 ExtractFileNameOnly(ProjectFileName) + CompiledExtension;
550 if not DirectoryIsWritable(ExtractFilePath(Result)) then
551 Result := GetAppConfigDir(False) + CompiledDir + DirectorySeparator + Name +
552 DirectorySeparator + ExtractFileNameOnly(ProjectFileName) + CompiledExtension;
553end;
554
555procedure TTarget.RunFromFile;
556var
557 CompiledFile: string;
558 FormConsole: TFormConsole;
559 Parameters: TStringArray;
560 I: Integer;
561begin
562 CompiledFile := GetCompileFileName;
563 if FileExists(CompiledFile) then
564 try
565 FormConsole := TFormConsole.Create(nil);
566 if ExecutorPath <> '' then begin
567 if not FileExists(ExecutorPath) then
568 raise Exception.Create(Format(SExecutorNotFound, [ExecutorPath]));
569 FormConsole.Executable := LongFileName(ExecutorPath);
570 Parameters := GetRunParams;
571 for I := 0 to Length(Parameters) - 1 do
572 FormConsole.Parameters.Add(Parameters[I]);
573 end else
574 FormConsole.Executable := LongFileName(CompiledFile);
575 FormConsole.ShowModal;
576 finally
577 FormConsole.Free;
578 end
579 else raise Exception.Create(Format(SCompiledFileNotFound, [CompiledFile]));
580end;
581
582function TTarget.GetRunParams: TStringArray;
583begin
584 Result := [LongFileName(GetCompileFileName)];
585end;
586
587procedure TTarget.Run;
588begin
589end;
590
591procedure TTarget.Pause;
592begin
593end;
594
595procedure TTarget.Stop;
596begin
597end;
598
599procedure TTarget.StepOver;
600begin
601end;
602
603procedure TTarget.StepInto;
604begin
605end;
606
607procedure TTarget.StepOut;
608begin
609end;
610
611procedure TTarget.RunToCursor(Pos: Integer);
612begin
613end;
614
615procedure TTarget.LoadFromRegistry(Context: TRegistryContext);
616begin
617 with TRegistryEx.Create do
618 try
619 CurrentContext := Context;
620 CompilerPath := ReadStringWithDefault('CompilerPath', CompilerPath);
621 ExecutorPath := ReadStringWithDefault('ExecutorPath', ExecutorPath);
622 Selected := ReadBoolWithDefault('Selected', Selected);
623 finally
624 Free;
625 end;
626end;
627
628procedure TTarget.SaveToRegistry(Context: TRegistryContext);
629begin
630 with TRegistryEx.Create do
631 try
632 CurrentContext := Context;
633 WriteString('CompilerPath', CompilerPath);
634 WriteString('ExecutorPath', ExecutorPath);
635 WriteBool('Selected', Selected);
636 finally
637 Free;
638 end;
639end;
640
641procedure TTarget.LoadProgram;
642begin
643end;
644
645function TTarget.SourceReadNext: Char;
646begin
647 Result := ' ';
648end;
649
650end.
651
Note: See TracBrowser for help on using the repository browser.