1{
2 /***************************************************************************
3                               lazconf.pp
4                             -------------------
5                           Lazarus Config Functions
6                   Initial Revision  : Tue Apr 18 22:10:00 CET 2000
7
8 ***************************************************************************/
9
10 ***************************************************************************
11 *                                                                         *
12 *   This source is free software; you can redistribute it and/or modify   *
13 *   it under the terms of the GNU General Public License as published by  *
14 *   the Free Software Foundation; either version 2 of the License, or     *
15 *   (at your option) any later version.                                   *
16 *                                                                         *
17 *   This code is distributed in the hope that it will be useful, but      *
18 *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
19 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
20 *   General Public License for more details.                              *
21 *                                                                         *
22 *   A copy of the GNU General Public License is available on the World    *
23 *   Wide Web at <http://www.gnu.org/copyleft/gpl.html>. You can also      *
24 *   obtain it by writing to the Free Software Foundation,                 *
25 *   Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1335, USA.   *
26 *                                                                         *
27 ***************************************************************************
28}
29
30{
31@author(Config Path Functions - Curtis White <cwhite@aracnet.com>)
32@created(18-Apr-2000)
33@lastmod(18-Apr-2000)
34
35This unit contains functions to manage OS specific configuration path
36information from within Lazarus.
37}
38unit LazConf;
39
40{$mode objfpc}{$H+}
41
42interface
43
44{$ifdef Trace}
45  {$ASSERTIONS ON}
46{$endif}
47
48uses
49  SysUtils, Classes,
50  // LazUtils
51  FileUtil, LazFileUtils, LazUTF8, LazLoggerBase,
52  // Codetools
53  DefineTemplates;
54
55const
56  LazarusVersionStr = {$I version.inc};
57
58function CompareLazarusVersion(V1, V2: string): integer;
59
60{ Config Path Functions }
61
62{ The primary config path is the local or user specific path.
63  If the primary config path does not exist, it will automatically be
64  created by the IDE.
65  The secondary config path is for templates. The IDE will never write to it.
66  If a config file is not found in the primary config file, Lazarus will
67  copy the template file from the secondary config file. If there is no
68  template file, the IDE will use defaults.
69}
70function GetPrimaryConfigPath: String;
71function GetSecondaryConfigPath: String;
72function CreatePrimaryConfigPath: boolean;
73procedure SetPrimaryConfigPath(const NewValue: String);
74procedure SetSecondaryConfigPath(const NewValue: String);
75procedure CopySecondaryConfigFile(const ShortFilename: String);
76function GetProjectSessionsConfigPath: String;
77
78function GetDefaultTestBuildDirectory: string;
79procedure GetDefaultTestBuildDirs(List: TStrings);
80// create a pascal file, which can be used to test the compiler
81function CreateCompilerTestPascalFilename: string;
82
83function FindDefaultExecutablePath(const Executable: string): string;
84procedure GetDefaultCompilerFilenames(List: TStrings); // list of standard paths of compiler on various distributions
85function FindDefaultCompilerPath: string; // full path of GetDefaultCompilerFilename
86function FindDefaultMakePath: string; // full path of "make"
87procedure GetDefaultMakeFilenames(List: TStrings); // list of standard paths of "make" on various distributions
88function GetDefaultFPCSrcDirectories: TStringList;
89function GetDefaultLazarusSrcDirectories: TStringList;
90
91// returns the standard executable extension (e.g '.exe')
92function GetExecutableExt(TargetOS: string = ''): string;
93function MakeStandardExeFilename(TargetOS, Filename: string): string;
94// returns the standard library extension (e.g '.dll' or '.dylib')
95function GetLibraryExt(TargetOS: string = ''): string;
96// returns the standard library prefix (e.g 'lib')
97function GetLibraryPrefix(TargetOS: string = ''): string;
98function MakeStandardLibFilename(TargetOS, Filename: string): string;
99
100// returns the standard file extension for compiled units (e.g '.ppu')
101function GetDefaultCompiledUnitExt({%H-}FPCVersion, {%H-}FPCRelease: integer): string;
102
103function OSLocksExecutables: boolean;
104
105// returns the default browser
106procedure GetDefaultBrowser(var Browser, Params: string);
107
108// Replace OnGetApplicationName, so that Application.Title
109// doesn't interfere with GetAppConfigDir and related.
110function GetLazarusApplicationName: string;
111
112type
113  TLazConfMacroFunc = procedure(var s: string);
114var
115  LazConfMacroFunc: TLazConfMacroFunc = nil;
116procedure LazConfSubstituteMacros(var s: string);
117procedure AddFilenameToList(List: TStrings; const Filename: string;
118  SkipEmpty: boolean = true);
119
120const
121  ExitCodeRestartLazarus = 99;
122
123implementation
124
125{$I lazconf.inc}
126
127procedure AddFilenameToList(List: TStrings; const Filename: string;
128  SkipEmpty: boolean);
129var
130  i: Integer;
131begin
132  if SkipEmpty and (Filename='') then exit;
133  for i:=0 to List.Count-1 do
134    if CompareFilenames(List[i],Filename)=0 then exit;
135  List.Add(Filename);
136end;
137
138function GetLazarusApplicationName: string;
139begin
140  Result := 'lazarus';
141end;
142
143procedure LazConfSubstituteMacros(var s: string);
144begin
145  if Assigned(LazConfMacroFunc) then
146    LazConfMacroFunc(s);
147end;
148
149{---------------------------------------------------------------------------
150  function CreateCompilerTestPascalFilename: string;
151 ---------------------------------------------------------------------------}
152function CreateCompilerTestPascalFilename: string;
153
154  function CreateFile(const Filename: string): boolean;
155  var
156    fs: TFileStream;
157  begin
158    if FileExistsUTF8(Filename) then exit(true);
159    Result:=false;
160    try
161      fs:=TFileStream.Create(Filename,fmCreate);
162      fs.Free;
163      Result:=true;
164    except
165    end;
166  end;
167
168begin
169  Result:=AppendPathDelim(GetPrimaryConfigPath)+'compilertest.pas';
170  if CreateFile(Result) then exit;
171  Result:=AppendPathDelim(ExpandFileNameUTF8(GetTempDir))+'compilertest.pas';
172  if CreateFile(Result) then exit;
173  Debugln('unable to create temporary file ',Result);
174  Result:='';
175end;
176
177function FindDefaultExecutablePath(const Executable: string): string;
178begin
179  if FilenameIsAbsolute(Executable) then
180    Result:=Executable
181  else
182    Result:=SearchFileInPath(Executable,'',
183                             GetEnvironmentVariableUTF8('PATH'),PathSeparator,
184                             sffFindProgramInPath);
185  Result:=TrimFilename(Result);
186end;
187
188function CompareLazarusVersion(V1, V2: string): integer;
189// compare decimal numbers in strings
190// For example
191//   '0.9.30' < '1.0'
192//   '1.0 RC1' < '1.0 RC2'
193//   '1.0 RC2' < '1.0'
194//   '1.0' < '1.1'
195//   '1.0' < '1.0-0'
196//   '1.0 RC2' < '1.0.1'
197//   '1.0-2' < '1.0.0'
198
199// Rules: 'RC' < EndOfString < '-' < '.'
200
201  function ReadNumber(var p: PChar): integer;
202  begin
203    if p^=#0 then exit(-1);
204
205    if p^ in ['0'..'9'] then begin
206      Result:=0;
207      while (p^ in ['0'..'9']) do begin
208        if Result<100000 then
209          Result:=Result*10+ord(p^)-ord('0');
210        inc(p);
211      end;
212    end else begin
213      while (p^ in [' ',#9]) do inc(p);
214      case p^ of
215      '-': Result:=1;
216      '.': Result:=2;
217      else Result:=-2; // for example 'RC'
218      end;
219      while not (p^ in [#0,'0'..'9']) do inc(p);
220    end;
221  end;
222
223var
224  p1: PChar;
225  p2: PChar;
226  Number1: Integer;
227  Number2: Integer;
228begin
229  if V1='' then begin
230    if V2='' then
231      exit(0)
232    else
233      exit(-1);
234  end else begin
235    if V2='' then
236      exit(1);
237  end;
238  p1:=PChar(V1);
239  p2:=PChar(V2);
240  while (p1^<>#0) or (p2^<>#0) do begin
241    Number1:=ReadNumber(p1);
242    Number2:=ReadNumber(p2);
243    if Number1>Number2 then exit(1);
244    if Number1<Number2 then exit(-1);
245  end;
246  Result:=0;
247end;
248
249{---------------------------------------------------------------------------
250  getPrimaryConfigPath function
251 ---------------------------------------------------------------------------}
252function GetPrimaryConfigPath: String;
253begin
254  Result := PrimaryConfigPath;
255end;
256
257{---------------------------------------------------------------------------
258  getSecondaryConfigPath function
259 ---------------------------------------------------------------------------}
260function GetSecondaryConfigPath: String;
261begin
262  Result := SecondaryConfigPath;
263end;
264
265{---------------------------------------------------------------------------
266  createPrimaryConfigPath procedure
267 ---------------------------------------------------------------------------}
268function CreatePrimaryConfigPath: boolean;
269begin
270  Result:=ForceDirectoriesUTF8(GetPrimaryConfigPath);
271end;
272
273{---------------------------------------------------------------------------
274  SetPrimaryConfigPath procedure
275 ---------------------------------------------------------------------------}
276procedure SetPrimaryConfigPath(const NewValue: String);
277var
278  NewExpValue: String;
279begin
280  NewExpValue:=ChompPathDelim(ExpandFileNameUTF8(NewValue));
281  if NewExpValue=PrimaryConfigPath then exit;
282  debugln('SetPrimaryConfigPath NewValue="',UTF8ToConsole(NewValue),'" -> "',UTF8ToConsole(NewExpValue),'"');
283  PrimaryConfigPath := NewExpValue;
284end;
285
286{---------------------------------------------------------------------------
287  SetSecondaryConfigPath procedure
288 ---------------------------------------------------------------------------}
289procedure SetSecondaryConfigPath(const NewValue: String);
290begin
291  debugln('SetSecondaryConfigPath NewValue="',UTF8ToConsole(NewValue),'" -> "',UTF8ToConsole(ExpandFileNameUTF8(NewValue)),'"');
292  SecondaryConfigPath := ChompPathDelim(ExpandFileNameUTF8(NewValue));
293end;
294
295{---------------------------------------------------------------------------
296  CopySecondaryConfigFile procedure
297 ---------------------------------------------------------------------------}
298procedure CopySecondaryConfigFile(const ShortFilename: String);
299var
300  PrimaryFilename, SecondaryFilename: string;
301begin
302  if ShortFilename='' then exit;
303  PrimaryFilename:=AppendPathDelim(GetPrimaryConfigPath)+ShortFilename;
304  SecondaryFilename:=AppendPathDelim(GetSecondaryConfigPath)+ShortFilename;
305  if (not FileExistsUTF8(PrimaryFilename))
306  and (FileExistsUTF8(SecondaryFilename)) then begin
307    debugln(['CopySecondaryConfigFile ',SecondaryFilename,' -> ',PrimaryFilename]);
308    if not CreatePrimaryConfigPath then begin
309      debugln(['WARNING: unable to create primary config directory "',GetPrimaryConfigPath,'"']);
310      exit;
311    end;
312    if not CopyFile(SecondaryFilename,PrimaryFilename) then begin
313      debugln(['WARNING: unable to copy config "',SecondaryFilename,'" to "',PrimaryFilename,'"']);
314      exit;
315    end;
316    InvalidateFileStateCache; // we have to invalidate cache in order FileExistsCached finds the new primary config file
317  end;
318end;
319
320function GetProjectSessionsConfigPath: String;
321begin
322  Result:=AppendPathDelim(GetPrimaryConfigPath)+'projectsessions';
323end;
324
325function GetExecutableExt(TargetOS: string): string;
326begin
327  if TargetOS='' then
328    TargetOS:=GetCompiledTargetOS;
329  if (CompareText(copy(TargetOS,1,3), 'win') = 0)
330  or (CompareText(copy(TargetOS,1,3), 'dos') = 0) then
331    Result:='.exe'
332  else if SameText(TargetOS, 'browser') or SameText(TargetOS,'nodejs') then
333    Result:='.js'
334  else if SameText(TargetOS, 'embedded') then
335      Result:='.elf'
336  else
337    Result:='';
338end;
339
340function MakeStandardExeFilename(TargetOS, Filename: string): string;
341var
342  StdExt: String;
343begin
344  Result:=Filename;
345  if TargetOS='' then
346    TargetOS:=GetCompiledTargetOS;
347  StdExt:=GetExecutableExt(TargetOS);
348  if StdExt='' then exit;
349  Result:=ChangeFileExt(Result,StdExt);
350end;
351
352function GetLibraryExt(TargetOS: string): string;
353begin
354  if TargetOS='' then
355    TargetOS:=GetCompiledTargetOS;
356  if CompareText(copy(TargetOS,1,3), 'win') = 0 then
357    Result:='.dll'
358  else if CompareText(TargetOS, 'darwin') = 0 then
359    Result:='.dylib'
360  else if CompareText(TargetOS, 'ios') = 0 then
361    Result:='.dylib'
362  else if (CompareText(TargetOS, 'linux') = 0)
363  or (CompareText(TargetOS, 'android') = 0)
364  or (CompareText(TargetOS, 'freebsd') = 0)
365  or (CompareText(TargetOS, 'openbsd') = 0)
366  or (CompareText(TargetOS, 'netbsd') = 0)
367  or (CompareText(TargetOS, 'dragonfly') = 0)
368  or (CompareText(TargetOS, 'haiku') = 0) then
369    Result:='.so'
370  else
371    Result:='';
372end;
373
374function GetLibraryPrefix(TargetOS: string): string;
375var
376  SrcOS: String;
377begin
378  if TargetOS='' then
379    TargetOS:=GetCompiledTargetOS;
380  Result:='';
381  if SameText(TargetOS, 'browser') or SameText(TargetOS,'nodejs') then
382    exit('.js');
383  SrcOS:=GetDefaultSrcOSForTargetOS(TargetOS);
384  if CompareText(SrcOS, 'unix') = 0 then
385    Result:='lib';
386end;
387
388function MakeStandardLibFilename(TargetOS, Filename: string): string;
389var
390  StdExt: String;
391  StdPrefix: String;
392begin
393  Result:=Filename;
394  if TargetOS='' then
395    TargetOS:=GetCompiledTargetOS;
396  // change extension
397  StdExt:=GetLibraryExt(TargetOS);
398  if StdExt<>'' then
399    Result:=ChangeFileExt(Result,StdExt);
400  // change prefix
401  StdPrefix:=GetLibraryPrefix(TargetOS);
402  if StdPrefix<>'' then
403    Result:=ExtractFilePath(Result)+StdPrefix+ExtractFileName(Result);
404  // lowercase
405  if (CompareText(TargetOS,'linux')=0)
406  or (CompareText(TargetOS,'freebsd')=0)
407  or (CompareText(TargetOS,'netbsd')=0)
408  or (CompareText(TargetOS,'openbsd')=0)
409  or (CompareText(TargetOS,'dragonfly')=0)
410  then
411    Result:=ExtractFilePath(Result)+lowercase(ExtractFileName(Result));
412end;
413
414function GetDefaultLazarusSrcDirectories: TStringList;
415var
416  i: Integer;
417begin
418  Result:=TStringList.Create;
419  for i:=low(DefaultLazarusSrcDirs) to high(DefaultLazarusSrcDirs) do
420    Result.Add(DefaultLazarusSrcDirs[i]);
421end;
422
423function GetDefaultFPCSrcDirectories: TStringList;
424var
425  i: Integer;
426begin
427  Result:=TStringList.Create;
428  for i:=low(DefaultFPCSrcDirs) to high(DefaultFPCSrcDirs) do
429    Result.Add(DefaultFPCSrcDirs[i]);
430end;
431
432initialization
433  Randomize;
434  InternalInit;
435
436end.
437
438
439