prefixer.pas 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840
  1. unit prefixer;
  2. {$mode ObjFPC}{$H+}
  3. interface
  4. uses
  5. Classes, SysUtils, PScanner, PParser, PasTree, strutils, regexpr;
  6. Type
  7. { We have to override abstract TPasTreeContainer methods.
  8. See utils/fpdoc/dglobals.pp for an implementation of TFPDocEngine,
  9. a "real" engine. }
  10. TSimpleEngine = class(TPasTreeContainer)
  11. public
  12. function CreateElement(AClass: TPTreeElement; const AName: String;
  13. AParent: TPasElement; AVisibility: TPasMemberVisibility;
  14. const ASourceFilename: String; ASourceLinenumber: Integer): TPasElement;
  15. override;
  16. function FindElement(const AName: String): TPasElement; override;
  17. end;
  18. { TPrefixer }
  19. TFileMode = (
  20. fmReplace, // new file replaces old completely.
  21. fmReplaceNamespace, // new file replaces old completely. Namespaces are used for uses clause.
  22. fmInclude, // new file includes old, units in uses clause replaced with new names
  23. fmIncludeNamespace // new file includes old. Namespaces are used for uses clause.
  24. );
  25. TIncludeNameMode = (
  26. inmIfdefElse, // Unit name in include file is set using {$IFDEF DEFINE} NEWNAME {$ELSE} OLDNAME {$ENDIF}
  27. inmIfndef // Unit clause is included in main file, it is skipped in include file using {$IFNDEF DEFINE} unit OLDNAME; {$ENDIF}
  28. );
  29. TPrefixLog = Procedure (Sender : TObject; aType : TEventType; const aMsg : String) of object;
  30. TPrefixer = class(TComponent)
  31. private
  32. FCasedFileNames: Boolean;
  33. FCreateBackups: Boolean;
  34. FDefine: String;
  35. FDestFileName: String;
  36. FDestUnitName: String;
  37. FFileName: String;
  38. FKnownNameSpaces: TStrings;
  39. FNameMode: TIncludeNameMode;
  40. FNameSpace: String;
  41. FOnLog: TPrefixLog;
  42. FParams: TStrings;
  43. FSkipDestFileName: Boolean;
  44. FSources : TStrings;
  45. FDottedSources : TStrings;
  46. FNewUses : TStrings;
  47. FUnitFileMode: TFileMode;
  48. FFullFileName : String;
  49. Protected
  50. Procedure DoLog(aType : TEventType; Const aMsg : String);
  51. Procedure DoLog(aType : TEventType; Const aFmt : String; aArgs : array of const);
  52. procedure AddNameSpaces(Src: TStrings; aUses: TPasUsesClause);
  53. procedure CorrectUnitName(aName: String; aLineNr: Integer);
  54. procedure DoParseLog(Sender: TObject; const Msg: String);
  55. procedure GetAdditionalUnits(aUnitNames: TStrings; aSource: String);
  56. function GetDefine: String;
  57. function GetDestFileName: String;
  58. function GetDestUnitName: String;
  59. function MaybeBackup(const aFileName: string): Boolean;
  60. function ParseSource(AEngine: TPasTreeContainer;
  61. const FPCCommandLine: array of String; OSTarget, CPUTarget: String;
  62. Options: TParseSourceOptions): TPasModule;
  63. function ReplaceUnits(const aLine: string; aUnitNames : TStrings): String;
  64. function ReplaceWord(aLine, aName, aFull: String): String;
  65. function FindWord(aName,aLine : String): Boolean;
  66. procedure ReworkUses(aSection: TPasSection);
  67. Public
  68. Constructor Create(aOwner : TComponent); override;
  69. Destructor Destroy; override;
  70. Procedure PrintUses(aSection : TPasSection; aShowFileName : Boolean = false);
  71. procedure Execute;
  72. procedure ReworkUses(aUses,aNewUses : TStrings);
  73. class function ExtractPrefix(const aRule: String): String;
  74. class function ApplyRule(const aFile,aCasedName,aRule : String; PrettyPrint : Boolean) : String;
  75. class function ApplyAliasRule(const aName, aRule: String): String;
  76. // Create backups of created/changed files ?
  77. Property CreateBackups : Boolean Read FCreateBackups Write FCreateBackups;
  78. // How to create the new file.
  79. Property UnitFileMode : TFileMode Read FUnitFileMode Write FUnitFileMode;
  80. // How to set the unit name in the case of an include file.
  81. Property IncludeUnitNameMode : TIncludeNameMode Read FNameMode Write FNameMode;
  82. // Define to use to protect dotted names. Default FPC_DOTTEDUNITS
  83. Property Define : String Read GetDefine Write FDefine;
  84. // Filename to process. For include modes, this file will be overwritten !
  85. Property FileName : String Read FFileName Write FFileName;
  86. // Do not write dotted Filename
  87. Property SkipDestFileName : Boolean Read FSkipDestFileName Write FSkipDestFileName;
  88. // Dotted Filename to produce (including path & extension). If not set, NameSpace.FileName is used.
  89. Property DestFileName : String Read GetDestFileName Write FDestFileName;
  90. // Filename to produce. If not set, DestFileName without extension is used.
  91. Property DestUnitName : String Read GetDestUnitName Write FDestUnitName;
  92. // Namespace to be used for this unit.
  93. Property NameSpace : String Read FNameSpace Write FNameSpace;
  94. // Namespaces for used units, in format UnitName=NameSpace
  95. Property KnownNameSpaces : TStrings Read FKnownNameSpaces;
  96. // Params needed to parse FileName
  97. Property Params : TStrings Read FParams;
  98. // if True, then the output files have the same case as the unit names.
  99. // If False, all filenames are lowercased.
  100. Property CasedFileNames : Boolean Read FCasedFileNames Write FCasedFileNames;
  101. // For messages
  102. Property OnLog : TPrefixLog Read FOnLog Write FOnLog;
  103. end;
  104. implementation
  105. function TSimpleEngine.CreateElement(AClass: TPTreeElement;
  106. const AName: String;
  107. AParent: TPasElement;
  108. AVisibility: TPasMemberVisibility;
  109. const ASourceFilename: String;
  110. ASourceLinenumber: Integer): TPasElement;
  111. begin
  112. // Writeln(AName,' : ',AClass.ClassName,' at ',ASourceFilename,':',ASourceLinenumber);
  113. Result := AClass.Create(AName, AParent);
  114. Result.Visibility := AVisibility;
  115. Result.SourceFilename := ASourceFilename;
  116. Result.SourceLinenumber := ASourceLinenumber;
  117. end;
  118. function TSimpleEngine.FindElement(const AName: String): TPasElement;
  119. begin
  120. { dummy implementation, see TFPDocEngine.FindElement for a real example }
  121. Result := nil;
  122. end;
  123. constructor TPrefixer.Create(aOwner: TComponent);
  124. begin
  125. inherited Create(aOwner);
  126. FKnownNameSpaces:=TStringList.Create;
  127. FParams:=TStringList.Create;
  128. FSources:=TStringList.Create;
  129. FDottedSources:=TStringList.Create;
  130. FNewuses:=TStringList.Create;
  131. FUnitFileMode:=fmInclude;
  132. FNameMode:=inmIfndef;
  133. end;
  134. destructor TPrefixer.Destroy;
  135. begin
  136. FreeAndNil(FKnownNameSpaces);
  137. FreeAndNil(FParams);
  138. FreeAndNil(FSources);
  139. FreeAndNil(FDottedSources);
  140. FreeAndNil(FNewuses);
  141. inherited Destroy;
  142. end;
  143. procedure TPrefixer.PrintUses(aSection: TPasSection; aShowFileName: Boolean);
  144. Var
  145. aUses : TPasUsesUnit;
  146. aName : string;
  147. begin
  148. if aSection=Nil then
  149. exit;
  150. for aUses in aSection.UsesClause do
  151. begin
  152. aName:='';
  153. if aShowFileName and assigned(aUses.InFileName) then
  154. aName:=AnsiDequotedStr(aUses.InFileName.Value,'''');
  155. if (aName='') and assigned(aUses.Expr) then
  156. aName:=aUses.Expr.GetDeclaration(False);
  157. if aName='' then
  158. aName:=aUses.Name;
  159. DoLog(etInfo,'%s, { location: %s:%d }',[aName,aUses.SourceFilename,aUses.SourceLinenumber]);
  160. end;
  161. end;
  162. function TPrefixer.ReplaceWord(aLine, aName, aFull: String): String;
  163. var
  164. RE : TRegExpr;
  165. begin
  166. RE:=TRegExpr.Create('\b'+aName+'\b');
  167. try
  168. Result:=RE.Replace(aLine,aFull);
  169. DoLog(etDebug, '%s: %s -> %s = %s',[aLine,aName,aFull,Result]);
  170. finally
  171. RE.Free;
  172. end;
  173. end;
  174. function TPrefixer.FindWord(aName, aLine: String): Boolean;
  175. var
  176. RE : TRegExpr;
  177. begin
  178. RE:=TRegExpr.Create('\b'+aName+'\b');
  179. try
  180. RE.ModifierI:=True;
  181. Result:=RE.Exec(aLine);
  182. DoLog(etDebug, '%s: %s = %s',[aLine,aName,BoolToStr(Result,'true','false')]);
  183. finally
  184. RE.Free;
  185. end;
  186. end;
  187. function TPrefixer.ReplaceUnits(const aLine: string; aUnitNames: TStrings): String;
  188. Var
  189. res,aName,aFull,aNameSpace,aUnit : String;
  190. idx : Integer;
  191. begin
  192. Res:=aLine;
  193. for aName in aUnitNames do
  194. begin
  195. Idx:=FKnownNameSpaces.IndexOfName(aName);
  196. if Idx<>-1 then
  197. begin
  198. FKnownNameSpaces.GetNameValue(Idx,aUnit,aNameSpace);
  199. if Copy(aNameSpace,1,1)='*' then
  200. aFull:=Copy(aNameSpace,2)
  201. else
  202. aFull:=aNameSpace+'.'+aUnit;
  203. Res:=ReplaceWord(Res,aName,aFull);
  204. end;
  205. end;
  206. Result:=Res;
  207. end;
  208. procedure TPrefixer.GetAdditionalUnits(aUnitNames : TStrings; aSource : String);
  209. Var
  210. aRE : TRegExpr;
  211. aWords : TStringList;
  212. aWord : string;
  213. begin
  214. awords:=nil;
  215. aRE:=TRegExpr.Create('(\w+)');
  216. Try
  217. aWords:=TstringList.Create;
  218. if aRe.Exec(aSource) then
  219. repeat
  220. aWord:=System.Copy(aSource, ARE.MatchPos[0], ARE.MatchLen[0]);
  221. if IndexText(aWord,['uses','define','undef','if','ifdef', 'endif','else'])=-1 then
  222. if (FKnownNameSpaces.IndexOfName(aWord)<>-1) then
  223. aUnitNames.Add(aWord); // Duplicates set to ignore
  224. until not Are.ExecNext;
  225. Finally
  226. aWords.Free;
  227. aRE.Free;
  228. end;
  229. end;
  230. procedure TPrefixer.ReworkUses(aSection: TPasSection);
  231. Var
  232. aUses : TPasUsesUnit;
  233. S,aName,aLine,FNUses,FNMain : String;
  234. aUnitNames : TStringList;
  235. // all 0-based
  236. I,Idx, FUses,FUsesEnd, FFirst,FLast : Integer;
  237. begin
  238. if (aSection=Nil)
  239. or (Length(aSection.UsesClause)=0)
  240. or ((Length(aSection.UsesClause)=1)
  241. and (SameText(aSection.UsesClause[0].Name,'System'))) then
  242. exit;
  243. FNMain:=ExtractFileName(FFileName);
  244. FFirst:=FSources.Count+1;
  245. FLast:=0;
  246. aUnitNames:=TStringList.Create;
  247. try
  248. aUnitNames.Sorted:=True;
  249. aUnitNames.Duplicates:=dupIgnore;
  250. for aUses in aSection.UsesClause do
  251. begin
  252. if aUses.SourceLinenumber-1>FLast then
  253. FLast:=aUses.SourceLinenumber-1;
  254. if aUses.SourceLinenumber-1<FFirst then
  255. FFirst:=aUses.SourceLinenumber-1;
  256. FNUses:=ExtractFileName(aUses.SourceFilename);
  257. aName:='';
  258. if (aName='') and assigned(aUses.Expr) then
  259. aName:=aUses.Expr.GetDeclaration(False);
  260. if aName='' then
  261. aName:=aUses.Name;
  262. if (FNUses<>FNMain) or (expandfilename(aUses.SourceFileName)<>FFullFileName) then
  263. Raise Exception.CreateFmt('Uses clause unit %s not in main unit: (uses: %s) <> %s',[aName,FNUses,FNMain]);
  264. aUnitNames.Add(aName);
  265. end;
  266. Fuses:=FFirst;
  267. if Fuses>=FSources.Count then
  268. FUses:=FSources.Count-1;
  269. While (FUses>=0) and (Pos('uses',lowerCase(FSources[FUses]))=0) do
  270. Dec(Fuses);
  271. FUsesEnd:=FLast; // Fuses is 0 bases
  272. While (FUsesEnd<FSources.Count) and (Pos(';',FSources[FUsesEnd])=0) do
  273. Inc(FusesEnd);
  274. DoLog(etDebug, 'Uses clause extends from %d: %s',[FUses,FSources[FUses]]);
  275. DoLog(etDebug, 'Uses clause extends to %d: %s',[FUsesEnd,FSources[FUsesEnd]]);
  276. S:='';
  277. For I:=Fuses to FUsesEnd do
  278. S:=S+#10+FSources[I];
  279. GetAdditionalUnits(aUnitNames,S);
  280. FNewuses.Clear;
  281. if UnitFileMode<>fmReplace then
  282. FNewuses.Add('{$IFDEF '+Define+'}');
  283. For Idx:=FUses to FUsesEnd do
  284. begin
  285. aLine:=FSources[Idx];
  286. If (Idx>=FFirst) and (Idx<=FLast) then
  287. begin
  288. aLine:=ReplaceUnits(aLine,aUnitNames);
  289. end;
  290. FNewUses.Add(aLine);
  291. end;
  292. // Check what we need to do with original sources
  293. if UnitFileMode<>fmReplace then
  294. begin
  295. FNewuses.Add('{$ELSE '+Define+'}');
  296. // Insert before uses
  297. FSources.Insert(FUsesEnd+1,'{$ENDIF '+Define+'}');
  298. end
  299. else
  300. begin
  301. // If we need to replace, we just remove all old lines before adding the new ones
  302. if UnitFileMode=fmReplace then
  303. For I:=FUsesEnd downto FUses do
  304. FSources.Delete(I);
  305. end;
  306. For I:=FNewUses.Count-1 downto 0 do
  307. FSources.Insert(FUses,FNewUses[i]);
  308. finally
  309. aUnitNames.Free;
  310. end;
  311. end;
  312. function TPrefixer.GetDefine: String;
  313. begin
  314. Result:=FDefine;
  315. if Result='' then
  316. Result:='FPC_DOTTEDUNITS';
  317. end;
  318. function TPrefixer.MaybeBackup(const aFileName: string): Boolean;
  319. Var
  320. BFN : String;
  321. FIn,Fout : TFileStream;
  322. begin
  323. Result:=FileExists(aFileName);
  324. if Result then
  325. begin
  326. BFN:=aFileName+'.bak';
  327. Fout:=Nil;
  328. Fin:=TFileStream.Create(aFilename,fmOpenRead or fmShareDenyWrite);
  329. try
  330. Fout:=TFileStream.Create(BFN,fmCreate);
  331. Fout.CopyFrom(FIn,0);
  332. finally
  333. Fin.Free;
  334. Fout.Free;
  335. end;
  336. end;
  337. end;
  338. function TPrefixer.GetDestFileName: String;
  339. Var
  340. DN, FN : String;
  341. begin
  342. Result:=FDestFileName;
  343. if Result='' then
  344. begin
  345. DN:=ExtractFilePath(FileName);
  346. FN:=ExtractFileName(FileName);
  347. if CasedFileNames then
  348. Result:=DN+NameSpace+'.'+FN
  349. else
  350. Result:=DN+LowerCase(NameSpace+'.'+FN);
  351. end;
  352. end;
  353. function TPrefixer.GetDestUnitName: String;
  354. begin
  355. Result:=FDestUnitName;
  356. if Result='' then
  357. Result:=ChangeFileExt(ExtractFileName(DestFileName),'');
  358. end;
  359. procedure TPrefixer.CorrectUnitName(aName : String; aLineNr : Integer);
  360. Var
  361. aLine,aReplace,aNewName : string;
  362. Idx : Integer;
  363. begin
  364. aNewName:=DestUnitName;
  365. if (aNewName=aName) then
  366. exit; // nothing to do.
  367. case IncludeUnitNameMode of
  368. inmIfdefElse:
  369. begin
  370. aLine:=FSources[aLineNr];
  371. aReplace:='{$IFDEF '+Define+'} '+aNewName+' {$ELSE} '+aName+' {$ENDIF}';
  372. aLine:=ReplaceWord(aLine,aName,aReplace);
  373. end;
  374. inmIfndef:
  375. begin
  376. // Look for ;
  377. idx:=aLineNr-1;
  378. While (Idx<FSources.Count) and (Pos(';',FSources[Idx])=0) do
  379. Inc(Idx);
  380. if (Idx<FSources.Count-1) then
  381. FSources.Insert(Idx+1,'{$ENDIF '+DEFINE+'}');
  382. // Look for unit
  383. idx:=aLineNr;
  384. if Idx>=FSources.Count then
  385. Idx:=FSources.Count-1;
  386. While (Idx>=0) and Not FindWord('unit',FSources[Idx]) do
  387. Dec(Idx);
  388. if Idx>=0 then
  389. FSources.Insert(Idx,'{$IFNDEF '+DEFINE+'}');
  390. end;
  391. end;
  392. end;
  393. procedure TPrefixer.DoParseLog(Sender: TObject; const Msg: String);
  394. begin
  395. DoLog(etDebug,Msg);
  396. end;
  397. procedure TPrefixer.DoLog(aType: TEventType; const aMsg: String);
  398. begin
  399. if assigned(FOnLog) then
  400. FOnLog(Self,aType,aMsg);
  401. end;
  402. procedure TPrefixer.DoLog(aType: TEventType; const aFmt: String;
  403. aArgs: array of const);
  404. begin
  405. DoLog(aType,Format(aFmt,aArgs));
  406. end;
  407. procedure TPrefixer.AddNameSpaces(Src : TStrings; aUses : TPasUsesClause);
  408. Var
  409. aUsed : TPasUsesUnit;
  410. aDirective,aName,aNameSpace,aUnit : String;
  411. idx : Integer;
  412. begin
  413. for aUsed in aUses do
  414. begin
  415. aName:='';
  416. if assigned(aUsed.Expr) then
  417. aName:=aUsed.Expr.GetDeclaration(False);
  418. if aName='' then
  419. aName:=aUsed.Name;
  420. Idx:=FKnownNameSpaces.IndexOfName(aName);
  421. if Idx<>-1 then
  422. begin
  423. FKnownNameSpaces.GetNameValue(Idx,aUnit,aNameSpace);
  424. aDirective:='{$NAMESPACE '+aNameSpace+'}';
  425. if Src.IndexOf(aDirective)=-1 then
  426. Src.Insert(0,aDirective);
  427. end;
  428. end;
  429. end;
  430. function TPrefixer.ParseSource(AEngine: TPasTreeContainer;
  431. const FPCCommandLine: array of String; OSTarget, CPUTarget: String;
  432. Options: TParseSourceOptions): TPasModule;
  433. var
  434. FileResolver: TBaseFileResolver;
  435. Parser: TPasParser;
  436. lFilename: String;
  437. Scanner: TPascalScanner;
  438. allowmem : Boolean;
  439. procedure ProcessCmdLinePart(S : String);
  440. var
  441. l,Len: Integer;
  442. begin
  443. if (S='') then
  444. exit;
  445. Len:=Length(S);
  446. if (s[1] = '-') and (len>1) then
  447. begin
  448. case s[2] of
  449. 'd': // -d define
  450. begin
  451. s:=Copy(s, 3, Len);
  452. Scanner.AddDefine(UpperCase(S));
  453. if s='allowmem' then
  454. AllowMem:=True;
  455. end;
  456. 'u': // -u undefine
  457. Scanner.RemoveDefine(UpperCase(Copy(s, 3, Len)));
  458. 'F': // -F
  459. if (len>2) and (s[3] = 'i') then // -Fi include path
  460. FileResolver.AddIncludePath(Copy(s, 4, Len));
  461. 'I': // -I include path
  462. FileResolver.AddIncludePath(Copy(s, 3, Len));
  463. 'S': // -S mode
  464. if (len>2) then
  465. begin
  466. l:=3;
  467. While L<=Len do
  468. begin
  469. case S[l] of
  470. 'c' : Scanner.Options:=Scanner.Options+[po_cassignments];
  471. 'd' : Scanner.SetCompilerMode('DELPHI');
  472. '2' : Scanner.SetCompilerMode('OBJFPC');
  473. 'h' : ; // do nothing
  474. end;
  475. inc(l);
  476. end;
  477. end;
  478. 'M' :
  479. begin
  480. delete(S,1,2);
  481. l:=pos(':',S);
  482. if (L<>0) and (UpperCase(Copy(S,1,l-1))='MODESWITCH') then
  483. begin
  484. Delete(S,1,l);
  485. if SameText(S,'externalclass') then
  486. Scanner.ReadOnlyModeSwitches:=Scanner.ReadOnlyModeSwitches+[msExternalClass];
  487. Scanner.SetModeSwitch(S);
  488. end
  489. else
  490. Scanner.SetCompilerMode(S);
  491. end;
  492. end;
  493. end else
  494. if lFilename <> '' then
  495. raise ENotSupportedException.Create(SErrMultipleSourceFiles)
  496. else
  497. lFilename := s;
  498. end;
  499. var
  500. S: String;
  501. opts : TPOptions;
  502. begin
  503. AllowMem:=False;
  504. if DefaultFileResolverClass=Nil then
  505. raise ENotImplemented.Create(SErrFileSystemNotSupported);
  506. Result := nil;
  507. FileResolver := nil;
  508. Scanner := nil;
  509. Parser := nil;
  510. try
  511. FileResolver := DefaultFileResolverClass.Create;
  512. if FileResolver is TFileResolver then
  513. TFileResolver(FileResolver).UseStreams:=poUseStreams in Options;
  514. Scanner := TPascalScanner.Create(FileResolver);
  515. Scanner.LogEvents:=AEngine.ScannerLogEvents;
  516. Scanner.OnLog:=AEngine.Onlog;
  517. if not (poSkipDefaultDefs in Options) then
  518. begin
  519. Scanner.AddDefine('FPK');
  520. Scanner.AddDefine('FPC');
  521. Scanner.AddDefine('FPC_LITTLE_ENDIAN');
  522. // TargetOS
  523. s := UpperCase(OSTarget);
  524. Scanner.AddDefine(s);
  525. Case s of
  526. 'LINUX' : Scanner.AddDefine('UNIX');
  527. 'FREEBSD' :
  528. begin
  529. Scanner.AddDefine('BSD');
  530. Scanner.AddDefine('UNIX');
  531. end;
  532. 'NETBSD' :
  533. begin
  534. Scanner.AddDefine('BSD');
  535. Scanner.AddDefine('UNIX');
  536. end;
  537. 'SUNOS' :
  538. begin
  539. Scanner.AddDefine('SOLARIS');
  540. Scanner.AddDefine('UNIX');
  541. end;
  542. 'GO32V2' : Scanner.AddDefine('DPMI');
  543. 'BEOS' : Scanner.AddDefine('UNIX');
  544. 'QNX' : Scanner.AddDefine('UNIX');
  545. 'AROS' : Scanner.AddDefine('HASAMIGA');
  546. 'MORPHOS' : Scanner.AddDefine('HASAMIGA');
  547. 'AMIGA' : Scanner.AddDefine('HASAMIGA');
  548. end;
  549. // TargetCPU
  550. s := UpperCase(CPUTarget);
  551. Scanner.AddDefine('CPU'+s);
  552. if (s='X86_64') then
  553. Scanner.AddDefine('CPU64')
  554. else
  555. Scanner.AddDefine('CPU32');
  556. end;
  557. Parser := TPasParser.Create(Scanner, FileResolver, AEngine);
  558. if (poSkipDefaultDefs in Options) then
  559. Parser.ImplicitUses.Clear;
  560. lFilename := '';
  561. Parser.LogEvents:=AEngine.ParserLogEvents;
  562. Parser.OnLog:=AEngine.Onlog;
  563. For S in FPCCommandLine do
  564. ProcessCmdLinePart(S);
  565. if lFilename = '' then
  566. raise Exception.Create(SErrNoSourceGiven);
  567. FileResolver.AddIncludePath(ExtractFilePath(lFileName));
  568. opts:=[po_AsmWhole,po_AsmPascalComments];
  569. if AllowMem then
  570. Include(opts,po_allowmem);
  571. opts:=opts+Scanner.options;
  572. Parser.Options:=Parser.Options+opts;
  573. Parser.OnLog:=@DoParseLog;
  574. Scanner.OpenFile(lFilename);
  575. Parser.ParseMain(Result);
  576. finally
  577. Parser.Free;
  578. Scanner.Free;
  579. FileResolver.Free;
  580. end;
  581. end;
  582. procedure TPrefixer.Execute;
  583. var
  584. M: TPasModule;
  585. P : TPasProgram absolute M;
  586. L : TPasLibrary absolute M;
  587. E: TPasTreeContainer;
  588. cmdline : Array of String;
  589. begin
  590. FFullFileName:=ExpandFileName(FFileName);
  591. cmdline:=Params.ToStringArray;
  592. CmdLine:=Concat(CmdLine,[FileName]);
  593. E := TSimpleEngine.Create;
  594. M := nil;
  595. try
  596. E.OnLog:=@DoParseLog;
  597. E.ParserLogEvents:=[pleImplementation,pleInterface];
  598. FSources.LoadFromFile(FFileName);
  599. FDottedSources.Clear;
  600. M := Self.ParseSource(E, cmdline, 'linux', 'i386',[]);
  601. if UnitFileMode in [fmInclude,fmIncludeNamespace] then
  602. begin
  603. if IncludeUnitNameMode=inmIfndef then
  604. FDottedSources.Add('unit '+DestUnitName+';');
  605. FDottedSources.Add('{$DEFINE '+Define+'}');
  606. end;
  607. if M is TPasProgram then
  608. begin
  609. if UnitFileMode in [fmReplace,fmInclude] then
  610. ReworkUses(P.ProgramSection)
  611. else
  612. AddNameSpaces(FSources,P.ProgramSection.UsesClause);
  613. end
  614. else if M is TPasLibrary then
  615. begin
  616. if UnitFileMode in [fmReplace,fmInclude] then
  617. ReworkUses(L.LibrarySection)
  618. else
  619. AddNameSpaces(FSources,L.LibrarySection.UsesClause);
  620. end
  621. else
  622. begin
  623. if UnitFileMode in [fmReplace,fmInclude] then
  624. begin
  625. ReworkUses(M.ImplementationSection);
  626. ReworkUses(M.InterfaceSection);
  627. CorrectUnitName(M.Name,M.SourceLinenumber);
  628. end
  629. else
  630. begin
  631. AddNamespaces(FDottedSources,M.ImplementationSection.UsesClause);
  632. AddNameSpaces(FDottedSources,M.InterfaceSection.UsesClause);
  633. end;
  634. end;
  635. if UnitFileMode in [fmReplace,fmReplaceNamespace] then
  636. begin
  637. MaybeBackup(DestFileName);
  638. FSources.SaveToFile(DestFileName);
  639. end
  640. else
  641. begin
  642. MaybeBackup(FileName);
  643. FSources.SaveToFile(FileName);
  644. if not SkipDestFileName then
  645. begin
  646. FDottedSources.Add('{$i '+ExtractFileName(FileName)+'}');
  647. MaybeBackup(DestFileName);
  648. FDottedSources.SaveToFile(DestFileName);
  649. end;
  650. end;
  651. finally
  652. FreeAndNil(M);
  653. FreeAndNil(E)
  654. end;
  655. end;
  656. procedure TPrefixer.ReworkUses(aUses, aNewUses: TStrings);
  657. Var
  658. S,aLine : String;
  659. aUnitNames : TStringList;
  660. I,Idx : Integer;
  661. begin
  662. aUnitNames:=TStringList.Create;
  663. try
  664. aUnitNames.Sorted:=True;
  665. aUnitNames.Duplicates:=dupIgnore;
  666. S:='';
  667. For I:=0 to aUses.Count-1 do
  668. S:=S+#10+aUses[I];
  669. GetAdditionalUnits(aUnitNames,S);
  670. aNewuses.Clear;
  671. aNewuses.Add('{$IFDEF '+Define+'}');
  672. For Idx:=0 to aUses.Count-1 do
  673. begin
  674. aLine:=aUses[Idx];
  675. aLine:=ReplaceUnits(aLine,aUnitNames);
  676. aNewUses.Add(aLine);
  677. end;
  678. // Add original
  679. aNewuses.Add('{$ELSE '+Define+'}');
  680. aNewuses.AddStrings(aUses);
  681. aNewuses.Add('{$ENDIF '+Define+'}');
  682. finally
  683. aUnitNames.Free;
  684. end;
  685. end;
  686. class function TPrefixer.ExtractPrefix(const aRule: String) : String;
  687. Var
  688. P : Integer;
  689. begin
  690. // *Prefix.UnitName
  691. if Copy(aRule,1,1)='*' then
  692. begin
  693. P:=Pos('.',aRule);
  694. Result:=Copy(aRule,2,P-2);
  695. end
  696. else
  697. begin
  698. // Prefix,UnitNamerule
  699. P:=Pos(',',aRule);
  700. if P=0 then
  701. P:=Length(aRule)+1;
  702. Result:=Copy(aRule,1,P-1);
  703. end;
  704. end;
  705. class function TPrefixer.ApplyAliasRule(const aName, aRule: String) : String;
  706. begin
  707. If Copy(aRule,1,1)='*' then
  708. Result:=Copy(aRule,2)
  709. else if aRule<>'' then
  710. Result:=aRule+'.'+aName
  711. else
  712. Result:=aName;
  713. end;
  714. class function TPrefixer.ApplyRule(const aFile, aCasedName,aRule: String;
  715. PrettyPrint: Boolean): String;
  716. Var
  717. p,len : Integer;
  718. aExt,aDir,aName,aPrefix : String;
  719. begin
  720. aPrefix:='';
  721. aDir:=ExtractFilePath(aFile);
  722. aExt:=ExtractFileExt(aFile);
  723. Result:=ExtractFileName(aFile);
  724. // *DottedUnitName
  725. // Prefix
  726. // Prefix,*UnitSuffix
  727. // Prefix,-DeleteFromOriginalAtStart
  728. // Prefix,DeleteFromOriginalAtEnd-
  729. P:=Pos(',',aRule);
  730. if P=0 then
  731. begin
  732. if aRule<>'' then
  733. if aRule[1]='*' then
  734. Result:=Copy(aRule,2)+aExt
  735. else if PrettyPrint and (aCasedName<>'') then
  736. Result:=aRule+'.'+aCasedName+aExt
  737. else
  738. aPrefix:=aRule+'.'
  739. end
  740. else
  741. begin
  742. aPrefix:=Copy(aRule,1,P-1)+'.';
  743. aName:=Copy(aRule,P+1);
  744. Len:=Length(AName);
  745. if Len>0 then
  746. begin
  747. Case aName[1] of
  748. '*' : Result:=Copy(aName,2)+ExtractFileExt(Result);
  749. '-' : if Pos(Copy(aName,2),Result)=1 then
  750. Delete(Result,1,Len-1);
  751. else
  752. if (aName[Len]='-') and (RPos(aName,Result)=Length(Result)-Len+1) then
  753. Result:=Copy(Result,1,Length(Result)-Len);
  754. end;
  755. end;
  756. end;
  757. if PrettyPrint then
  758. Result[1]:=Upcase(Result[1]);
  759. Result:=aDir+aPrefix+Result;
  760. end;
  761. end.