source: trunk/Target.pas

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