summaryrefslogtreecommitdiff
path: root/examples/apps/ide/src/project.pas
blob: f31e724f9f2f9c05cfe36e70717bf4a0249d5169 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
unit Project;

{$mode objfpc}{$H+}

interface

uses
  Classes, SysUtils, UnitList, fpg_base, fpg_iniutils;

type
  TBooleanGrid = array of array of Boolean;

  TProject = class(TObject)
  private
    FMakeOptionsGrid: TBooleanGrid;
    FProjectName: TfpgString;
    FMainUnit: TfpgString;
    FUnitDirs: TStringList;
    FUnitDirsGrid: TBooleanGrid;
    FUnitList: TUnitList;
    FIniFile: TfpgINIFile;
    FProjectDir: TfpgString;
    FTargetFile: TfpgString;
    FDefaultMake: integer;
    FMakeOptions: TStringList;
    FMacroNames: TStringList;
    FUnitOutputDir: TfpgString;
  public
    constructor Create;
    destructor  Destroy; override;
    function    Save(const AFile: TfpgString = ''): Boolean;
    function    Load(AProjectFile: TfpgString): Boolean;
    function    GenerateCmdLine(const AShowOnly: Boolean = False; const ABuildMode: integer = -1): TfpgString;
    procedure   ClearAndInitMakeOptions(const ASize: integer);
    procedure   ClearAndInitUnitDirsGrid(const ASize: integer);
    property    ProjectDir: TfpgString read FProjectDir write FProjectDir;
    property    ProjectName: TfpgString read FProjectName write FProjectName;
    property    MainUnit: TfpgString read FMainUnit write FMainUnit;
    property    TargetFile: TfpgString read FTargetFile write FTargetFile;
    property    UnitList: TUnitList read FUnitList;
    property    DefaultMake: integer read FDefaultMake write FDefaultMake;
    property    MakeOptions: TStringList read FMakeOptions;
    property    MakeOptionsGrid: TBooleanGrid read FMakeOptionsGrid write FMakeOptionsGrid;
    property    MacroNames: TStringList read FMacroNames;
    property    UnitDirs: TStringList read FUnitDirs;
    property    UnitOutputDir: TfpgString read FUnitOutputDir write FUnitOutputDir;
    property    UnitDirsGrid: TBooleanGrid read FUnitDirsGrid write FUnitDirsGrid;
  end;


// lazy-mans singleton
function GProject: TProject;

procedure FreeProject;


implementation

uses
  ideconst
  ,ideutils
  ,fpg_utils
  ;


var
  uProject: TProject;

function GProject: TProject;
begin
  if not Assigned(uProject) then
    uProject := TProject.Create;
  Result := uProject;
end;

procedure FreeProject;
begin
  uProject.Free;
  uProject := nil;
end;


{ TProject }

constructor TProject.Create;
begin
  inherited Create;
  FUnitList := TUnitList.Create;
  FMakeOptions := TStringList.Create;
  FMacroNames := TStringList.Create;
  FUnitDirs := TStringList.Create;
end;

destructor TProject.Destroy;
begin
  FUnitDirs.Free;
  FMacroNames.Free;
  FMakeOptions.Free;
  FUnitList.Free;
  FIniFile.Free;
  inherited Destroy;
end;

function TProject.Save(const AFile: TfpgString = ''): Boolean;
var
  c, j: integer;
  s: TfpgString;
  lDelim: TfpgString;

  procedure SaveList(AList: TStringList; const CName, IName: TfpgString);
  var
    i: integer;
  begin
    FIniFile.WriteInteger(cProjectOptions, CName, AList.Count);
    for i := 0 to AList.Count-1 do
      FIniFile.WriteString(cProjectOptions, IName + IntToStr(i+1), AList[i]);
  end;

begin
  Result := False;
  if (AFile = '') and (ProjectName = '') then
    raise Exception.Create('Project name has not been specified yet');

  if not Assigned(FIniFile) then
  begin
    if AFile = '' then
      FIniFile := TfpgINIFile.CreateExt(ProjectDir + ProjectName + cProjectExt)
    else
      FIniFile := TfpgINIFile.CreateExt(AFile);
  end
  else
  begin
    if AFile <> '' then
    begin
      FIniFile.Free;
      FIniFile := TfpgINIFile.CreateExt(AFile);
    end;
  end;

  if AFile <> '' then
    ProjectName := fpgExtractFileName(AFile);

  FIniFile.WriteString(cProjectOptions, 'ProjectName', ProjectName);
  FIniFile.WriteString(cProjectOptions, 'MainUnit', MainUnit);
  FIniFile.WriteString(cProjectOptions, 'TargetFile', TargetFile);
  FIniFile.WriteInteger(cProjectOptions, 'DefaultMake', DefaultMake);
  FIniFile.WriteString(cProjectOptions, 'UnitOutputDir', UnitOutputDir);

  // Process the Make (compiler param) options
  { first delete old items in ini file }
  c := FIniFile.ReadInteger(cProjectOptions, 'MakeOptionsCount', 0);
  for j := 1 to c do
    FIniFile.DeleteKey(cProjectOptions, cINIMakeOption + IntToStr(j));
  { no lets save new info }
  SaveList(MakeOptions, 'MakeOptionsCount', cINIMakeOption);
  for j := 0 to MakeOptions.Count-1 do
  begin
    s := '';
    lDelim := '';
    for c := 0 to 5 do
    begin
      if MakeOptionsGrid[c, j] then  // True = 1, False = 0
        s := s + lDelim + '1'
      else
        s := s + lDelim + '0';
      lDelim := ',';
    end;
    FIniFile.WriteString(cProjectOptions, cINIMakeOptionGrid + IntToStr(j+1), s);
  end;

  // macros definitions
  SaveList(MacroNames, 'MacroCount', 'Macro');

  // unit search directories
  { first delete old items in ini file }
  c := FIniFile.ReadInteger(cProjectOptions, 'UnitDirsCount', 0);
  for j := 1 to c do
    FIniFile.DeleteKey(cProjectOptions, cINIUnitDir + IntToStr(j));
  SaveList(UnitDirs, 'UnitDirsCount', cINIUnitDir);
  for j := 0 to UnitDirs.Count-1 do
  begin
    s := '';
    lDelim := '';
    for c := 0 to 9 do
    begin
      if UnitDirsGrid[c, j] then  // True = 1, False = 0
        s := s + lDelim + '1'
      else
        s := s + lDelim + '0';
      lDelim := ',';
    end;
    FIniFile.WriteString(cProjectOptions, cINIUnitDirGrid + IntToStr(j+1), s);
  end;

  // Unit file list
  FIniFile.WriteInteger(cUnits, 'UnitCount', UnitList.Count);
  for j := 0 to UnitList.Count-1 do
  begin
    s := UnitList[j].FileName;
    FIniFile.WriteString(cUnits, 'Unit' + IntToStr(j+1),
        Format('%s,%s', [ExtractRelativepath(ProjectDir, s), BoolToStr(UnitList[j].Opened, False)]));
  end;

  Result := True;
end;

function TProject.Load(AProjectFile: TfpgString): Boolean;
var
  a: string;
  s: TfpgString;
  j: integer;
  l: integer;
  sl: TStringList;
  u: TUnit;

  // CName = xxxCount & IName is the Item name
  procedure LoadList(ASection: TfpgString; AList: TStringList; const CName, IName: TfpgString);
  var
    c: integer;
    i: integer;
  begin
    c := FIniFile.ReadInteger(ASection, CName, 0);
    for i := 0 to c-1 do
    begin
      s := FIniFile.ReadString(ASection, IName + IntToStr(i+1), '');
      if s <> '' then
        AList.Add(s);
    end;
  end;

begin
  Result := False;
  if AProjectFile = '' then
    raise Exception.Create('You need to specify a Project filename');

  if not Assigned(FIniFile) then
    FIniFile := TfpgINIFile.CreateExt(AProjectFile);

  ProjectDir := fpgExtractFilePath(AProjectFile);
  ProjectName := FIniFile.ReadString(cProjectOptions, 'ProjectName', ChangeFileExt(fpgExtractFileName(AProjectFile), ''));
  MainUnit := FIniFile.ReadString(cProjectOptions, 'MainUnit', '');
  TargetFile := FIniFile.ReadString(cProjectOptions, 'TargetFile', '');
  DefaultMake := FIniFile.ReadInteger(cProjectOptions, 'DefaultMake', 0);
  UnitOutputDir := FIniFile.ReadString(cProjectOptions, 'UnitOutputDir', 'units/'+cMacro_Target+'/');

  // Load make options
  LoadList(cProjectOptions, MakeOptions, 'MakeOptionsCount', 'MakeOption');
  sl := TStringList.Create;
  try
    LoadList(cProjectOptions, sl, 'MakeOptionsCount', cINIMakeOptionGrid);
    SetLength(FMakeOptionsGrid, 6, MakeOptions.Count);    // 6 columns by X rows
    for j := 0 to sl.Count-1 do
    begin
      s := sl[j];
      for l := 0 to 5 do  // we know we only have 6 columns
      begin
        a := tiToken(s, ',', l+1);
        MakeOptionsGrid[l, j] := Boolean(StrToInt(a));  // 1 = True, 0 = False
      end;
    end;
  finally
    sl.Free;
  end;

  // Load Macro definitions
  LoadList(cProjectOptions, MacroNames, 'MacroCount', 'Macro');

  // Load Unit search dirs
  LoadList(cProjectOptions, UnitDirs, 'UnitDirsCount', 'UnitDir');
  sl := TStringList.Create;
  try
    LoadList(cProjectOptions, sl, 'UnitDirsCount', 'UnitDirEnabled');
    SetLength(FUnitDirsGrid, 10, UnitDirs.Count);    // 10 columns by X rows
    for j := 0 to sl.Count-1 do
    begin
      s := sl[j];
      for l := 0 to 9 do  // we know we only have 10 columns
      begin
        a := tiToken(s, ',', l+1);
        UnitDirsGrid[l, j] := Boolean(StrToInt(a));  // 1 = True, 0 = False
      end;
    end;
  finally
    sl.Free;
  end;

  // Load Unit file list
  sl := TStringList.Create;
  try
    LoadList(cUnits, sl, 'UnitCount', 'Unit');
    for j := 0 to sl.Count-1 do
    begin
      u := TUnit.Create;
      s := tiToken(sl[j], ',', 1);
      u.FileName := fpgExpandFileName(ProjectDir + s);
      u.Opened := Boolean(StrToInt(tiToken(sl[j], ',', 2)));  // 1 = True, 0 = False
      UnitList.Add(u);
    end;
  finally
    sl.Free;
  end;

  Result := True;
end;

function TProject.GenerateCmdLine(const AShowOnly: Boolean; const ABuildMode: integer): TfpgString;
var
  c: TfpgString;
  b: integer;
  eol: TfpgString;
  i: integer;
begin
  if AShowOnly then
    eol := LineEnding
  else
    eol := '';
  if ABuildMode = -1 then
    b := DefaultMake
  else
    b := ABuildMode;

  // include dirs
  for i := 0 to UnitDirs.Count-1 do
    if UnitDirsGrid[b, i] and UnitDirsGrid[7, i] then
      c := c + ' -Fi' + UnitDirs[i] + eol;
  // unit dirs
  for i := 0 to UnitDirs.Count-1 do
    if UnitDirsGrid[b, i] and UnitDirsGrid[6, i] then
      c := c + ' -Fu' + UnitDirs[i] + eol;
  // unit output dir
  if UnitOutputDir <> '' then
    c := c + ' -FU' + UnitOutputDir + eol;
  // make option - compiler flags
  for i := 0 to MakeOptions.Count-1 do
    if MakeOptionsGrid[b, i] then
      c := c + ' ' + MakeOptions[i];
  // target output file
  if TargetFile <> '' then
    c := c + ' -o' + TargetFile;
  // unit to start compilation
  c := c + ' ' + MainUnit;

  Result := c;
end;

procedure TProject.ClearAndInitMakeOptions(const ASize: integer);
begin
  FMakeOptions.Clear;
  SetLength(FMakeOptionsGrid, 0, 0);    // free items
  SetLength(FMakeOptionsGrid, 6, ASize);    // 6 columns by X rows
end;

procedure TProject.ClearAndInitUnitDirsGrid(const ASize: integer);
begin
  FUnitDirs.Clear;
  SetLength(FUnitDirsGrid, 0, 0); // free items
  SetLength(FUnitDirsGrid, 10, ASize);   // 10 columns by X rows
end;


initialization
  uProject := nil;

finalization
  FreeProject;

end.