prefixer.pas 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843
  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, NS : String;
  341. begin
  342. Result:=FDestFileName;
  343. if Result='' then
  344. begin
  345. DN:=ExtractFilePath(FileName);
  346. FN:=ExtractFileName(FileName);
  347. NS:=NameSpace;
  348. if NS<>'' then
  349. NS:=NS+'.';
  350. if CasedFileNames then
  351. Result:=DN+NS+FN
  352. else
  353. Result:=DN+LowerCase(NS+FN);
  354. end;
  355. end;
  356. function TPrefixer.GetDestUnitName: String;
  357. begin
  358. Result:=FDestUnitName;
  359. if Result='' then
  360. Result:=ChangeFileExt(ExtractFileName(DestFileName),'');
  361. end;
  362. procedure TPrefixer.CorrectUnitName(aName : String; aLineNr : Integer);
  363. Var
  364. aLine,aReplace,aNewName : string;
  365. Idx : Integer;
  366. begin
  367. aNewName:=DestUnitName;
  368. if (aNewName=aName) then
  369. exit; // nothing to do.
  370. case IncludeUnitNameMode of
  371. inmIfdefElse:
  372. begin
  373. aLine:=FSources[aLineNr];
  374. aReplace:='{$IFDEF '+Define+'} '+aNewName+' {$ELSE} '+aName+' {$ENDIF}';
  375. aLine:=ReplaceWord(aLine,aName,aReplace);
  376. end;
  377. inmIfndef:
  378. begin
  379. // Look for ;
  380. idx:=aLineNr-1;
  381. While (Idx<FSources.Count) and (Pos(';',FSources[Idx])=0) do
  382. Inc(Idx);
  383. if (Idx<FSources.Count-1) then
  384. FSources.Insert(Idx+1,'{$ENDIF '+DEFINE+'}');
  385. // Look for unit
  386. idx:=aLineNr;
  387. if Idx>=FSources.Count then
  388. Idx:=FSources.Count-1;
  389. While (Idx>=0) and Not FindWord('unit',FSources[Idx]) do
  390. Dec(Idx);
  391. if Idx>=0 then
  392. FSources.Insert(Idx,'{$IFNDEF '+DEFINE+'}');
  393. end;
  394. end;
  395. end;
  396. procedure TPrefixer.DoParseLog(Sender: TObject; const Msg: String);
  397. begin
  398. DoLog(etDebug,Msg);
  399. end;
  400. procedure TPrefixer.DoLog(aType: TEventType; const aMsg: String);
  401. begin
  402. if assigned(FOnLog) then
  403. FOnLog(Self,aType,aMsg);
  404. end;
  405. procedure TPrefixer.DoLog(aType: TEventType; const aFmt: String;
  406. aArgs: array of const);
  407. begin
  408. DoLog(aType,Format(aFmt,aArgs));
  409. end;
  410. procedure TPrefixer.AddNameSpaces(Src : TStrings; aUses : TPasUsesClause);
  411. Var
  412. aUsed : TPasUsesUnit;
  413. aDirective,aName,aNameSpace,aUnit : String;
  414. idx : Integer;
  415. begin
  416. for aUsed in aUses do
  417. begin
  418. aName:='';
  419. if assigned(aUsed.Expr) then
  420. aName:=aUsed.Expr.GetDeclaration(False);
  421. if aName='' then
  422. aName:=aUsed.Name;
  423. Idx:=FKnownNameSpaces.IndexOfName(aName);
  424. if Idx<>-1 then
  425. begin
  426. FKnownNameSpaces.GetNameValue(Idx,aUnit,aNameSpace);
  427. aDirective:='{$NAMESPACE '+aNameSpace+'}';
  428. if Src.IndexOf(aDirective)=-1 then
  429. Src.Insert(0,aDirective);
  430. end;
  431. end;
  432. end;
  433. function TPrefixer.ParseSource(AEngine: TPasTreeContainer;
  434. const FPCCommandLine: array of String; OSTarget, CPUTarget: String;
  435. Options: TParseSourceOptions): TPasModule;
  436. var
  437. FileResolver: TBaseFileResolver;
  438. Parser: TPasParser;
  439. lFilename: String;
  440. Scanner: TPascalScanner;
  441. allowmem : Boolean;
  442. procedure ProcessCmdLinePart(S : String);
  443. var
  444. l,Len: Integer;
  445. begin
  446. if (S='') then
  447. exit;
  448. Len:=Length(S);
  449. if (s[1] = '-') and (len>1) then
  450. begin
  451. case s[2] of
  452. 'd': // -d define
  453. begin
  454. s:=Copy(s, 3, Len);
  455. Scanner.AddDefine(UpperCase(S));
  456. if s='allowmem' then
  457. AllowMem:=True;
  458. end;
  459. 'u': // -u undefine
  460. Scanner.RemoveDefine(UpperCase(Copy(s, 3, Len)));
  461. 'F': // -F
  462. if (len>2) and (s[3] = 'i') then // -Fi include path
  463. FileResolver.AddIncludePath(Copy(s, 4, Len));
  464. 'I': // -I include path
  465. FileResolver.AddIncludePath(Copy(s, 3, Len));
  466. 'S': // -S mode
  467. if (len>2) then
  468. begin
  469. l:=3;
  470. While L<=Len do
  471. begin
  472. case S[l] of
  473. 'c' : Scanner.Options:=Scanner.Options+[po_cassignments];
  474. 'd' : Scanner.SetCompilerMode('DELPHI');
  475. '2' : Scanner.SetCompilerMode('OBJFPC');
  476. 'h' : ; // do nothing
  477. end;
  478. inc(l);
  479. end;
  480. end;
  481. 'M' :
  482. begin
  483. delete(S,1,2);
  484. l:=pos(':',S);
  485. if (L<>0) and (UpperCase(Copy(S,1,l-1))='MODESWITCH') then
  486. begin
  487. Delete(S,1,l);
  488. if SameText(S,'externalclass') then
  489. Scanner.ReadOnlyModeSwitches:=Scanner.ReadOnlyModeSwitches+[msExternalClass];
  490. Scanner.SetModeSwitch(S);
  491. end
  492. else
  493. Scanner.SetCompilerMode(S);
  494. end;
  495. end;
  496. end else
  497. if lFilename <> '' then
  498. raise ENotSupportedException.Create(SErrMultipleSourceFiles)
  499. else
  500. lFilename := s;
  501. end;
  502. var
  503. S: String;
  504. opts : TPOptions;
  505. begin
  506. AllowMem:=False;
  507. if DefaultFileResolverClass=Nil then
  508. raise ENotImplemented.Create(SErrFileSystemNotSupported);
  509. Result := nil;
  510. FileResolver := nil;
  511. Scanner := nil;
  512. Parser := nil;
  513. try
  514. FileResolver := DefaultFileResolverClass.Create;
  515. if FileResolver is TFileResolver then
  516. TFileResolver(FileResolver).UseStreams:=poUseStreams in Options;
  517. Scanner := TPascalScanner.Create(FileResolver);
  518. Scanner.LogEvents:=AEngine.ScannerLogEvents;
  519. Scanner.OnLog:=AEngine.Onlog;
  520. if not (poSkipDefaultDefs in Options) then
  521. begin
  522. Scanner.AddDefine('FPK');
  523. Scanner.AddDefine('FPC');
  524. Scanner.AddDefine('FPC_LITTLE_ENDIAN');
  525. // TargetOS
  526. s := UpperCase(OSTarget);
  527. Scanner.AddDefine(s);
  528. Case s of
  529. 'LINUX' : Scanner.AddDefine('UNIX');
  530. 'FREEBSD' :
  531. begin
  532. Scanner.AddDefine('BSD');
  533. Scanner.AddDefine('UNIX');
  534. end;
  535. 'NETBSD' :
  536. begin
  537. Scanner.AddDefine('BSD');
  538. Scanner.AddDefine('UNIX');
  539. end;
  540. 'SUNOS' :
  541. begin
  542. Scanner.AddDefine('SOLARIS');
  543. Scanner.AddDefine('UNIX');
  544. end;
  545. 'GO32V2' : Scanner.AddDefine('DPMI');
  546. 'BEOS' : Scanner.AddDefine('UNIX');
  547. 'QNX' : Scanner.AddDefine('UNIX');
  548. 'AROS' : Scanner.AddDefine('HASAMIGA');
  549. 'MORPHOS' : Scanner.AddDefine('HASAMIGA');
  550. 'AMIGA' : Scanner.AddDefine('HASAMIGA');
  551. end;
  552. // TargetCPU
  553. s := UpperCase(CPUTarget);
  554. Scanner.AddDefine('CPU'+s);
  555. if (s='X86_64') then
  556. Scanner.AddDefine('CPU64')
  557. else
  558. Scanner.AddDefine('CPU32');
  559. end;
  560. Parser := TPasParser.Create(Scanner, FileResolver, AEngine);
  561. if (poSkipDefaultDefs in Options) then
  562. Parser.ImplicitUses.Clear;
  563. lFilename := '';
  564. Parser.LogEvents:=AEngine.ParserLogEvents;
  565. Parser.OnLog:=AEngine.Onlog;
  566. For S in FPCCommandLine do
  567. ProcessCmdLinePart(S);
  568. if lFilename = '' then
  569. raise Exception.Create(SErrNoSourceGiven);
  570. FileResolver.AddIncludePath(ExtractFilePath(lFileName));
  571. opts:=[po_AsmWhole,po_AsmPascalComments];
  572. if AllowMem then
  573. Include(opts,po_allowmem);
  574. opts:=opts+Scanner.options;
  575. Parser.Options:=Parser.Options+opts;
  576. Parser.OnLog:=@DoParseLog;
  577. Scanner.OpenFile(lFilename);
  578. Parser.ParseMain(Result);
  579. finally
  580. Parser.Free;
  581. Scanner.Free;
  582. FileResolver.Free;
  583. end;
  584. end;
  585. procedure TPrefixer.Execute;
  586. var
  587. M: TPasModule;
  588. P : TPasProgram absolute M;
  589. L : TPasLibrary absolute M;
  590. E: TPasTreeContainer;
  591. cmdline : Array of String;
  592. begin
  593. FFullFileName:=ExpandFileName(FFileName);
  594. cmdline:=Params.ToStringArray;
  595. CmdLine:=Concat(CmdLine,[FileName]);
  596. E := TSimpleEngine.Create;
  597. M := nil;
  598. try
  599. E.OnLog:=@DoParseLog;
  600. E.ParserLogEvents:=[pleImplementation,pleInterface];
  601. FSources.LoadFromFile(FFileName);
  602. FDottedSources.Clear;
  603. M := Self.ParseSource(E, cmdline, 'linux', 'i386',[]);
  604. if UnitFileMode in [fmInclude,fmIncludeNamespace] then
  605. begin
  606. if IncludeUnitNameMode=inmIfndef then
  607. FDottedSources.Add('unit '+DestUnitName+';');
  608. FDottedSources.Add('{$DEFINE '+Define+'}');
  609. end;
  610. if M is TPasProgram then
  611. begin
  612. if UnitFileMode in [fmReplace,fmInclude] then
  613. ReworkUses(P.ProgramSection)
  614. else
  615. AddNameSpaces(FSources,P.ProgramSection.UsesClause);
  616. end
  617. else if M is TPasLibrary then
  618. begin
  619. if UnitFileMode in [fmReplace,fmInclude] then
  620. ReworkUses(L.LibrarySection)
  621. else
  622. AddNameSpaces(FSources,L.LibrarySection.UsesClause);
  623. end
  624. else
  625. begin
  626. if UnitFileMode in [fmReplace,fmInclude] then
  627. begin
  628. ReworkUses(M.ImplementationSection);
  629. ReworkUses(M.InterfaceSection);
  630. CorrectUnitName(M.Name,M.SourceLinenumber);
  631. end
  632. else
  633. begin
  634. AddNamespaces(FDottedSources,M.ImplementationSection.UsesClause);
  635. AddNameSpaces(FDottedSources,M.InterfaceSection.UsesClause);
  636. end;
  637. end;
  638. if UnitFileMode in [fmReplace,fmReplaceNamespace] then
  639. begin
  640. MaybeBackup(DestFileName);
  641. FSources.SaveToFile(DestFileName);
  642. end
  643. else
  644. begin
  645. MaybeBackup(FileName);
  646. FSources.SaveToFile(FileName);
  647. if not SkipDestFileName then
  648. begin
  649. FDottedSources.Add('{$i '+ExtractFileName(FileName)+'}');
  650. MaybeBackup(DestFileName);
  651. FDottedSources.SaveToFile(DestFileName);
  652. end;
  653. end;
  654. finally
  655. FreeAndNil(M);
  656. FreeAndNil(E)
  657. end;
  658. end;
  659. procedure TPrefixer.ReworkUses(aUses, aNewUses: TStrings);
  660. Var
  661. S,aLine : String;
  662. aUnitNames : TStringList;
  663. I,Idx : Integer;
  664. begin
  665. aUnitNames:=TStringList.Create;
  666. try
  667. aUnitNames.Sorted:=True;
  668. aUnitNames.Duplicates:=dupIgnore;
  669. S:='';
  670. For I:=0 to aUses.Count-1 do
  671. S:=S+#10+aUses[I];
  672. GetAdditionalUnits(aUnitNames,S);
  673. aNewuses.Clear;
  674. aNewuses.Add('{$IFDEF '+Define+'}');
  675. For Idx:=0 to aUses.Count-1 do
  676. begin
  677. aLine:=aUses[Idx];
  678. aLine:=ReplaceUnits(aLine,aUnitNames);
  679. aNewUses.Add(aLine);
  680. end;
  681. // Add original
  682. aNewuses.Add('{$ELSE '+Define+'}');
  683. aNewuses.AddStrings(aUses);
  684. aNewuses.Add('{$ENDIF '+Define+'}');
  685. finally
  686. aUnitNames.Free;
  687. end;
  688. end;
  689. class function TPrefixer.ExtractPrefix(const aRule: String) : String;
  690. Var
  691. P : Integer;
  692. begin
  693. // *Prefix.UnitName
  694. if Copy(aRule,1,1)='*' then
  695. begin
  696. P:=Pos('.',aRule);
  697. Result:=Copy(aRule,2,P-2);
  698. end
  699. else
  700. begin
  701. // Prefix,UnitNamerule
  702. P:=Pos(',',aRule);
  703. if P=0 then
  704. P:=Length(aRule)+1;
  705. Result:=Copy(aRule,1,P-1);
  706. end;
  707. end;
  708. class function TPrefixer.ApplyAliasRule(const aName, aRule: String) : String;
  709. begin
  710. If Copy(aRule,1,1)='*' then
  711. Result:=Copy(aRule,2)
  712. else if aRule<>'' then
  713. Result:=aRule+'.'+aName
  714. else
  715. Result:=aName;
  716. end;
  717. class function TPrefixer.ApplyRule(const aFile, aCasedName,aRule: String;
  718. PrettyPrint: Boolean): String;
  719. Var
  720. p,len : Integer;
  721. aExt,aDir,aName,aPrefix : String;
  722. begin
  723. aPrefix:='';
  724. aDir:=ExtractFilePath(aFile);
  725. aExt:=ExtractFileExt(aFile);
  726. Result:=ExtractFileName(aFile);
  727. // *DottedUnitName
  728. // Prefix
  729. // Prefix,*UnitSuffix
  730. // Prefix,-DeleteFromOriginalAtStart
  731. // Prefix,DeleteFromOriginalAtEnd-
  732. P:=Pos(',',aRule);
  733. if P=0 then
  734. begin
  735. if aRule<>'' then
  736. if aRule[1]='*' then
  737. Result:=Copy(aRule,2)+aExt
  738. else if PrettyPrint and (aCasedName<>'') then
  739. Result:=aRule+'.'+aCasedName+aExt
  740. else
  741. aPrefix:=aRule+'.'
  742. end
  743. else
  744. begin
  745. aPrefix:=Copy(aRule,1,P-1)+'.';
  746. aName:=Copy(aRule,P+1);
  747. Len:=Length(AName);
  748. if Len>0 then
  749. begin
  750. Case aName[1] of
  751. '*' : Result:=Copy(aName,2)+ExtractFileExt(Result);
  752. '-' : if Pos(Copy(aName,2),Result)=1 then
  753. Delete(Result,1,Len-1);
  754. else
  755. if (aName[Len]='-') and (RPos(aName,Result)=Length(Result)-Len+1) then
  756. Result:=Copy(Result,1,Length(Result)-Len);
  757. end;
  758. end;
  759. end;
  760. if PrettyPrint then
  761. Result[1]:=Upcase(Result[1]);
  762. Result:=aDir+aPrefix+Result;
  763. end;
  764. end.