source: trunk/UKConfig.pas

Last change on this file was 16, checked in by chronos, 8 years ago
  • Added: Preparation for filtering of non visible nodes.
File size: 30.5 KB
Line 
1unit UKConfig;
2
3// Kconfig format specification:
4// https://www.kernel.org/doc/Documentation/kbuild/kconfig-language.txt
5
6{$mode Delphi}{$H+}
7
8interface
9
10uses
11 Classes, SysUtils, Contnrs, FileUtil, ComCtrls;
12
13type
14 TValueType = (vtNone, vtBool, vtTristate, vtString, vtInt, vtHex, vtChoice,
15 vtComment);
16
17 TMenuNode = class;
18 TLoadTreeOption = (toShowSystem);
19 TLoadTreeOptions = set of TLoadTreeOption;
20 TObjectMove = record
21 Source: TObject;
22 NewParent: TObject;
23 end;
24 TObjectMoves = array of TObjectMove;
25
26 TExpOperator = (eoAnd, eoOr);
27
28 { TExpression }
29
30 TExpression = class
31 ExpOperator: TExpOperator;
32 Items: TObjectList; // TList<TExpression>
33 Parent: TExpression;
34 function Show: String; virtual;
35 function CanBeTrue: Boolean; virtual;
36 procedure GetList(List: TStringList); virtual;
37 constructor Create; virtual;
38 destructor Destroy; override;
39 end;
40
41 TCompareType = (ctNone, ctEqual, ctNotEqual);
42
43 { TExpressionVar }
44
45 TExpressionVar = class(TExpression)
46 Name: string;
47 //Node: TMenuNode;
48 Negative: Boolean;
49 Compare: TCompareType;
50 Value: string;
51 function CompareValue: Boolean;
52 function CanBeTrue: Boolean; override;
53 constructor Create; override;
54 procedure GetList(List: TStringList); override;
55 function Show: String; override;
56 end;
57
58 { TMenuNode }
59
60 TMenuNode = class
61 private
62 function GetVisible: Boolean;
63 public
64 ID: string;
65 Name: string;
66 DependsOn: TExpression;
67 Selects: TStringList;
68 Description: TStringList;
69 Items: TObjectList; // TList<TMenuNode>
70 Parent: TMenuNode;
71 //NewParent: TMenuNode;
72 DefaultValue: string;
73 ValueType: TValueType;
74 RangeMin: string;
75 RangeMax: string;
76 MenuConfig: Boolean;
77 Optional: Boolean;
78 constructor Create; virtual;
79 destructor Destroy; override;
80 procedure ChangeParent(NewParent: TMenuNode);
81 procedure AddLastToList(List: TObjectList);
82 function GetName: string; virtual;
83 function GetAbsoluteName: string; virtual;
84 procedure LoadTreeNode(Node: TTreeNode; Options: TLoadTreeOptions = []); virtual;
85 procedure LoadStats(List: TStrings); virtual;
86 procedure GetNodes(List: TStrings); virtual;
87 procedure Search(Text: string; List: TObjectList); virtual;
88 procedure SaveToList(List: TStrings); virtual;
89 function PrepareMoveList(Lookup: TStringList; var List: TObjectMoves): Boolean; virtual;
90 function GetCount: Integer; virtual;
91 function GetTopNode: TMenuNode; virtual;
92 function FindNode(ID: string): TMenuNode; virtual;
93 function CanBeVisible: Boolean;
94 property Visible: Boolean read GetVisible;
95 end;
96
97 TOnLogEvent = procedure (Text: string) of object;
98
99 { TConfigMenu }
100
101 TConfigMenu = class
102 private
103 CurrentMenu: TMenuNode;
104 ConditionStack: TStringList;
105 FArch: string;
106 FOnLog: TOnLogEvent;
107 ParseFileName: string;
108 LineNumber: Integer;
109 function GetTopCondition: string;
110 procedure Log(Text: string);
111 function IsWhiteSpace(Character: Char): Boolean;
112 function IsAlpha(Character: Char): Boolean;
113 function IsAlphaNumeric(Character: Char): Boolean;
114 function IsNumeric(Character: Char): Boolean;
115 function IsIdentifier(Name: string): Boolean;
116 function IsSpecialSymbol(Character: Char): Boolean;
117 procedure Expect(var Text: string; Token: string);
118 function GetNextToken(var Text: string): string;
119 procedure ParseExpression(Node: TMenuNode; Line: string);
120 procedure ParseFile(FileName: string);
121 procedure ParseConfig;
122 procedure ParseMakeFile;
123 function GetLog: string;
124 procedure SetArch(AValue: string);
125 public
126 TopNode: TMenuNode;
127 BaseDir: string;
128 Version: string;
129 procedure OptionListNames(List: TStringList);
130 procedure PrepareMoveList(var List: TObjectMoves);
131 procedure CompareStringLists(List1, List2: TStringList; Missing1,
132 Missing2: TStrings);
133 procedure LoadFromDir(Dir: string);
134 property OnLog: TOnLogEvent read FOnLog write FOnLog;
135 constructor Create;
136 destructor Destroy; override;
137 property Arch: string read FArch write SetArch;
138 end;
139
140implementation
141
142{ TExpressionVar }
143
144function TExpressionVar.CompareValue: Boolean;
145begin
146
147end;
148
149function TExpressionVar.CanBeTrue: Boolean;
150begin
151 if Compare = ctNone then begin
152
153 end else
154 if Compare = ctEqual then begin
155 Result := Result and CompareValue;
156 end else
157 if Compare = ctNotEqual then begin
158 Result := Result and not CompareValue;
159 end;
160// Result := Node.CanBeVisible;
161end;
162
163constructor TExpressionVar.Create;
164begin
165 inherited;
166 Compare := ctNone;
167 Value := '';
168 Negative := False;
169 Name := '';
170end;
171
172procedure TExpressionVar.GetList(List: TStringList);
173begin
174 inherited GetList(List);
175 List.Add(Name);
176end;
177
178function TExpressionVar.Show: String;
179begin
180 Result := Name;
181 if Negative then Result := '!' + Result;
182 if Compare = ctEqual then Result := Result + ' = ' + Value;
183 if Compare = ctNotEqual then Result := Result + ' != ' + Value;
184end;
185
186{ TExpression }
187
188function TExpression.Show: String;
189var
190 Operand: string;
191 I: Integer;
192begin
193 Result := '';
194 if ExpOperator = eoAnd then Operand := '&&';
195 if ExpOperator = eoOr then Operand := '||';
196 for I := 0 to Items.Count - 1 do begin
197 Result := Result + TExpression(Items[I]).Show;
198 if I < (Items.Count - 1) then Result := Result + ' ' + Operand + ' ';
199 end;
200 if Assigned(Parent) then Result := '(' + Result + ')';
201end;
202
203function TExpression.CanBeTrue: Boolean;
204var
205 I: Integer;
206begin
207 if ExpOperator = eoAnd then begin
208 Result := True;
209 for I := 0 to Items.Count - 1 do
210 Result := Result and TExpression(Items[I]).CanBeTrue;
211 end else
212 if ExpOperator = eoOr then begin
213 Result := False;
214 for I := 0 to Items.Count - 1 do
215 Result := Result or TExpression(Items[I]).CanBeTrue;
216 end;
217end;
218
219procedure TExpression.GetList(List: TStringList);
220var
221 I: Integer;
222begin
223 for I := 0 to Items.Count - 1 do
224 TExpression(Items[I]).GetList(List);
225end;
226
227constructor TExpression.Create;
228begin
229 Items := TObjectList.Create;
230 Parent := nil;
231 ExpOperator := eoAnd;
232end;
233
234destructor TExpression.Destroy;
235begin
236 Items.Free;
237 inherited Destroy;
238end;
239
240{ TMenuNode }
241
242function TMenuNode.GetVisible: Boolean;
243var
244 I: Integer;
245begin
246 Result := True;
247 //for I := 0 to Depends.Count - 1 do
248 //if Depends[I]
249end;
250
251constructor TMenuNode.Create;
252begin
253 Name := '';
254 ID := '';
255 Parent := nil;
256 DependsOn := TExpression.Create;
257 Description := TStringList.Create;
258 Selects := TStringList.Create;
259 Items := TObjectList.Create;
260 MenuConfig := False;
261end;
262
263destructor TMenuNode.Destroy;
264begin
265 DependsOn.Free;
266 Items.Free;
267 Description.Free;
268 Selects.Free;
269 inherited Destroy;
270end;
271
272procedure TMenuNode.ChangeParent(NewParent: TMenuNode);
273var
274 LastOwnState: Boolean;
275begin
276 try
277 LastOwnState := Parent.Items.OwnsObjects;
278 Parent.Items.OwnsObjects := False;
279 Parent.Items.Remove(Self);
280 finally
281 Parent.Items.OwnsObjects := LastOwnState;
282 end;
283 Parent := NewParent;
284 NewParent.Items.Add(Self);
285end;
286
287procedure TMenuNode.AddLastToList(List: TObjectList);
288begin
289 List.Add(Self);
290 if Items.Count > 0 then begin
291 TMenuNode(Items.Last).AddLastToList(List);
292 end;
293end;
294
295function TMenuNode.GetName: string;
296begin
297 Result := Name;
298 if Result = '' then Result := ID;
299end;
300
301function TMenuNode.GetAbsoluteName: string;
302begin
303 if Assigned(Parent) and Assigned(Parent.Parent) then Result := Parent.GetAbsoluteName + ' - ';
304 Result := Result + GetName;
305end;
306
307procedure TMenuNode.LoadTreeNode(Node: TTreeNode; Options: TLoadTreeOptions);
308var
309 I: Integer;
310 NewNode: TTreeNode;
311begin
312 for I := 0 to Items.Count - 1 do
313 with TMenuNode(Items[I]) do begin
314 if (Name <> '') or ((Name = '') and (toShowSystem in Options)) then begin
315 NewNode := Node.TreeNodes.AddChild(Node, GetName);
316 NewNode.Data := TMenuNode(Self.Items[I]);
317 LoadTreeNode(NewNode, Options);
318 end else LoadTreeNode(Node, Options);
319 end;
320end;
321
322procedure TMenuNode.LoadStats(List: TStrings);
323begin
324 with List do begin
325 Clear;
326 Add('ID: ' + ID);
327 Add('Name: ' + Name);
328 Add('Depends on: ' + DependsOn.Show);
329 Add('Selects: ' + Selects.Text);
330 Add('Description: ' + Description.Text);
331 Add('Value type: ' + IntToStr(Integer(ValueType)));
332 Add('Default value: ' + DefaultValue);
333 Add('Path: ' + GetAbsoluteName);
334 end;
335end;
336
337procedure TMenuNode.GetNodes(List: TStrings);
338var
339 I: Integer;
340begin
341 if ID <> '' then List.AddObject(ID, Self);
342 for I := 0 to Items.Count - 1 do
343 with TMenuNode(Items[I]) do begin
344 GetNodes(List);
345 end;
346end;
347
348procedure TMenuNode.Search(Text: string; List: TObjectList);
349var
350 I: Integer;
351begin
352 if (Pos(Text, ID) > 0) or (Pos(Text, Name) > 0) then List.Add(Self);
353 for I := 0 to Items.Count - 1 do
354 TMenuNode(Items[I]).Search(Text, List);
355end;
356
357procedure TMenuNode.SaveToList(List: TStrings);
358var
359 I: Integer;
360begin
361 if ID <> '' then List.AddObject(ID, Self);
362 for I := 0 to Items.Count - 1 do
363 with TMenuNode(Items[I]) do begin
364 SaveToList(List);
365 end;
366end;
367
368function TMenuNode.PrepareMoveList(Lookup: TStringList; var List: TObjectMoves): Boolean;
369var
370 I: Integer;
371 J: Integer;
372 NewMove: TObjectMove;
373 Index: Integer;
374 LatestNode: TMenuNode;
375 CheckList: TObjectList;
376 DepList: TStringList;
377begin
378 Result := False;
379 try
380 CheckList := TObjectList.Create;
381 CheckList.OwnsObjects := False;
382 DepList := TStringList.Create;
383 if Assigned(Parent) then begin
384 Index := Parent.Items.IndexOf(Self);
385 if Index = -1 then raise Exception.Create('Node not found in parent');
386 if Index >= 1 then TMenuNode(Parent.Items[Index - 1]).AddLastToList(CheckList);
387 DepList.Clear;
388 DependsOn.GetList(DepList);
389 for I := CheckList.Count - 1 downto 0 do begin
390 for J := 0 to DepList.Count - 1 do
391 if Trim(DepList[J]) <> '' then begin
392 if (DepList[J] = TMenuNode(CheckList[I]).ID) and (TMenuNode(CheckList[I]).ValueType <> vtChoice) then begin
393 ChangeParent(TMenuNode(CheckList[I]));
394 Result := True;
395 Break;
396 end;
397 end;
398 if Result then Break;
399 end;
400 end;
401 finally
402 DepList.Free;
403 CheckList.Free;
404 end;
405
406 I := 0;
407 while I < Items.Count do
408 with TMenuNode(Items[I]) do begin
409 if not PrepareMoveList(Lookup, List) then Inc(I);
410 end;
411end;
412
413function TMenuNode.GetCount: Integer;
414var
415 I: Integer;
416begin
417 Result := 1;
418 for I := 0 to Items.Count - 1 do
419 with TMenuNode(Items[I]) do begin
420 Result := Result + GetCount;
421 end;
422end;
423
424function TMenuNode.GetTopNode: TMenuNode;
425begin
426 if Assigned(Parent) then Result := Parent.GetTopNode
427 else Result := Self;
428end;
429
430function TMenuNode.FindNode(ID: string): TMenuNode;
431var
432 I: Integer;
433begin
434 Result := nil;
435 if Self.ID = ID then Result := Self
436 else begin
437 I := 0;
438 while (I < Items.Count) do begin
439 Result := TMenuNode(Items[I]).FindNode(ID);
440 if Assigned(Result) then Break;
441 Inc(I);
442 end;
443 end;
444end;
445
446function TMenuNode.CanBeVisible: Boolean;
447begin
448 // TODO
449 // Check if it can be selected manually if manual option
450 // Check if it can be selected automatically by other option
451
452end;
453
454{ TConfigMenu }
455
456function TConfigMenu.GetTopCondition: string;
457begin
458 if ConditionStack.Count > 0 then
459 Result := ConditionStack[ConditionStack.Count - 1]
460 else Result := '';
461end;
462
463procedure TConfigMenu.Log(Text: string);
464begin
465 if Assigned(FOnLog) then FOnLog(Text);
466end;
467
468function TConfigMenu.IsWhiteSpace(Character: Char): Boolean;
469begin
470 Result := (Character >= #0) and (Character <= ' ');
471end;
472
473function TConfigMenu.IsAlpha(Character: Char): Boolean;
474begin
475 Result := ((Character >= 'a') and (Character <= 'z'))
476 or ((Character >= 'A') and (Character <= 'Z'));
477end;
478
479function TConfigMenu.IsAlphaNumeric(Character: Char): Boolean;
480begin
481 Result := IsAlpha(Character) or IsNumeric(Character);
482end;
483
484function TConfigMenu.IsNumeric(Character: Char): Boolean;
485begin
486 Result := (Character >= '0') and (Character <= '9');
487end;
488
489function TConfigMenu.IsIdentifier(Name: string): Boolean;
490var
491 I: Integer;
492begin
493 Result := True;
494 for I := 1 to Length(Name) do
495 if not (IsAlphaNumeric(Name[I]) or (Name[I] = '_')) then begin
496 Result := False;
497 Break;
498 end;
499end;
500
501function TConfigMenu.IsSpecialSymbol(Character: Char): Boolean;
502begin
503 Result := (Character = '!') or (Character = '=') or (Character = '(') or
504 (Character = ')') or (Character = '-') or (Character = '|') or (Character = '&');
505end;
506
507procedure TConfigMenu.Expect(var Text: string; Token: string);
508begin
509 if GetNextToken(Text) <> Token then
510 raise Exception.Create('Expected "' + Token + '"');
511end;
512
513function TConfigMenu.GetNextToken(var Text: string): string;
514var
515 I: Integer;
516 Terminate: Boolean;
517 State: (stNormal, stWhiteSpace, stString, stSpecialSymbol, stIdent,
518 stString2);
519begin
520 I := 1;
521 Terminate := False;
522 Result := '';
523 State := stNormal;
524 while (I <= Length(Text)) and not Terminate do begin
525 if State = stWhiteSpace then begin
526 if not IsWhiteSpace(Text[I]) then begin
527 State := stNormal;
528 Terminate := True;
529 end;
530 end else
531 if State = stString then begin
532 if Text[I] = '"' then begin
533 State := stNormal;
534 Terminate := True;
535 Inc(I);
536 end else Result := Result + Text[I];
537 end else
538 if State = stString2 then begin
539 if Text[I] = '''' then begin
540 State := stNormal;
541 Terminate := True;
542 Inc(I);
543 end else Result := Result + Text[I];
544 end else
545 if State = stSpecialSymbol then begin
546 if not IsSpecialSymbol(Text[I]) or (Result = '(') or (Result = ')') or
547 ((Result = '!') and (Text[I] <> '=')) or (Result = '||') or
548 (Result = '&&') then begin
549 State := stNormal;
550 Terminate := True;
551 end else Result := Result + Text[I];
552 end else
553 if State = stIdent then begin
554 if not (IsAlphanumeric(Text[I]) or (Text[I] = '_')) then begin
555 State := stNormal;
556 Terminate := True;
557 end else Result := Result + Text[I];
558 end else begin
559 if IsWhiteSpace(Text[I]) then State := stWhiteSpace
560 else if Text[I] = '"' then State := stString
561 else if Text[I] = '''' then State := stString2
562 else if Text[I] = '#' then begin
563 // Comment
564 Text := '';
565 Terminate := True;
566 end
567 else if IsSpecialSymbol(Text[I]) then begin
568 State := stSpecialSymbol;
569 Result := Result + Text[I];
570 end
571 else if IsAlphaNumeric(Text[I]) then begin
572 State := stIdent;
573 Result := Result + Text[I];
574 end
575 else Result := Result + Text[I];
576 end;
577 if not Terminate then Inc(I);
578 end;
579 Delete(Text, 1, I - 1);
580end;
581
582procedure TConfigMenu.ParseExpression(Node: TMenuNode; Line: string);
583var
584 Token: string;
585 Exp: TExpression;
586 ExpVar: TExpressionVar;
587 ExpNew: TExpression;
588 Neg: Boolean;
589 Compare: TCompareType;
590begin
591 Line := Trim(Line);
592 Exp := Node.DependsOn;
593 Neg := False;
594 while Line <> '' do begin
595 Token := GetNextToken(Line);
596 if Token = '||' then begin
597 Exp.ExpOperator := eoOr;
598 end else
599 if Token = '&&' then begin
600 Exp.ExpOperator := eoAnd;
601 end else
602 if Token = '!' then begin
603 Neg := True;
604 Continue;
605 end else
606 if Token = '(' then begin
607 ExpNew := TExpression.Create;
608 ExpNew.Parent := Exp;
609 Exp.Items.Add(ExpNew);
610 Exp := ExpNew;
611 Neg := False;
612 end else
613 if Token = ')' then begin
614 if Assigned(Exp.Parent) then Exp := Exp.Parent
615 else raise Exception.Create('Cannot go to expression parent.' + GetLog);
616 Neg := False;
617 end else
618 if Token = '' then begin
619 // White space
620 end else
621 if Token = '=' then begin
622 ExpVar.Compare := ctEqual;
623 Token := GetNextToken(Line);
624 ExpVar.Value := Token;
625 Continue;
626 end else
627 if Token = '!=' then begin
628 ExpVar.Compare := ctNotEqual;
629 Token := GetNextToken(Line);
630 ExpVar.Value := Token;
631 Continue;
632 end else
633 if IsIdentifier(Token) then begin
634 ExpVar := TExpressionVar.Create;
635 ExpVar.Negative := Neg;
636 ExpVar.Name := Token;
637 ExpVar.Compare := Compare;
638 Exp.Items.Add(ExpVar);
639 Neg := False;
640 end else
641 raise Exception.Create('Unknown token "' + Token + '".' + GetLog);
642 end;
643
644 {// Just try to load symbol list. Do not parse expression
645 while Trim(Line) <> '' do begin
646 Parameter := GetNextToken(Line);
647 if Copy(Parameter, 1, 1) = '!' then Delete(Parameter, 1, 1);
648 if Copy(Parameter, 1, 1) = '(' then Delete(Parameter, 1, 1);
649 if Copy(Parameter, Length(Parameter), 1) = ')' then Delete(Parameter, Length(Parameter), 1);
650 Parameter := Trim(Parameter);
651 if (Parameter <> '&&') and (Parameter <> '||') then
652 Node.Depends.Add(Parameter);
653 end;
654 }
655
656end;
657
658function TConfigMenu.GetLog: string;
659begin
660 Result := ' ' + ParseFileName + ':' + IntToStr(LineNumber);
661end;
662
663procedure TConfigMenu.SetArch(AValue: string);
664begin
665 if FArch = AValue then Exit;
666 FArch := AValue;
667 LoadFromDir(BaseDir);
668end;
669
670procedure TConfigMenu.ParseFile(FileName: string);
671var
672 Content: TStringList;
673 I: Integer;
674 J: Integer;
675 Command: string;
676 Parameter: string;
677 Line: string;
678 State: (stNormal, stConfig, stHelp, stHelpStart);
679 NewItem: TMenuNode;
680 NewMenu: TMenuNode;
681 MergedLines: string;
682 HelpIndent: Integer;
683 LineIndent: Integer;
684 NewExp: TExpressionVar;
685const
686 TabSize = 8;
687begin
688 ParseFileName := FileName;
689 Log('FILE ' + FileName);
690 try
691 Content := TStringList.Create;
692 Content.LoadFromFile(FileName);
693 State := stNormal;
694 NewItem := nil;
695 MergedLines := '';
696 HelpIndent := 0;
697 for I := 0 to Content.Count - 1 do begin
698 LineNumber := I + 1;
699 Line := MergedLines + Content[I];
700 MergedLines := '';
701 LineIndent := 0;
702 J := 1;
703 while (J <= Length(Line)) and IsWhiteSpace(Line[J]) do begin
704 if Line[J] = #9 then LineIndent := (Trunc(LineIndent / TabSize) + 1) * TabSize
705 else Inc(LineIndent);
706 Inc(J);
707 end;
708 if (State = stHelp) and (Trim(Line) <> '') then begin
709 State := stHelpStart;
710 if LineIndent = 0 then State := stNormal;
711 HelpIndent := LineIndent;
712 if (State = stHelpStart) and (HelpIndent = 0) then
713 raise Exception.Create('Error during help parsing.' + GetLog);
714 end;
715 if (State = stHelpStart) and (Trim(Line) <> '') and (LineIndent < HelpIndent) then
716 State := stNormal;
717
718 if (Copy(Trim(Line), 1, 1) = '#') or (Copy(Trim(Line), 1, 2) = '\#') then begin
719 // Skip comments
720 Continue;
721 end;
722 if Copy(TrimRight(Line), Length(Line), 1) = '\' then begin
723 // Merge lines
724 Line := TrimRight(Line);
725 Delete(Line, Length(Line), 1);
726 MergedLines := Line;
727 Continue;
728 end;
729 Line := Trim(Line);
730 if Line = '' then begin
731 // Skip empty non help lines
732 Continue;
733 end;
734 Command := GetNextToken(Line);
735 if (State = stHelp) or (State = stHelpStart) then begin
736 NewItem.Description.Add(Trim(Content[I]));
737 end else
738 if (Command = 'bool') or (Command = 'boolean') then begin
739 Expect(Line, '');
740 Parameter := GetNextToken(Line);
741 if not Assigned(NewItem) then raise Exception.Create('Item not defined.' + GetLog);
742 NewItem.ValueType := vtBool;
743 NewItem.Name := Parameter;
744 end else
745 if Command = 'def_bool' then begin
746 Expect(Line, '');
747 Parameter := GetNextToken(Line);
748 if not Assigned(NewItem) then raise Exception.Create('Item not defined.' + GetLog);
749 NewItem.DefaultValue := Parameter;
750 NewItem.ValueType := vtBool;
751 end else
752 if Command = 'tristate' then begin
753 Expect(Line, '');
754 Parameter := GetNextToken(Line);
755 if not Assigned(NewItem) then raise Exception.Create('Item not defined.' + GetLog);
756 NewItem.Name := Parameter;
757 NewItem.ValueType := vtTristate;
758 end else
759 if Command = 'def_tristate' then begin
760 Expect(Line, '');
761 Parameter := GetNextToken(Line);
762 if not Assigned(NewItem) then raise Exception.Create('Item not defined.' + GetLog);
763 NewItem.Name := Parameter;
764 end else
765 if Command = 'string' then begin
766 Expect(Line, '');
767 Parameter := GetNextToken(Line);
768 if not Assigned(NewItem) then raise Exception.Create('Item not defined.' + GetLog);
769 NewItem.Name := Parameter;
770 NewItem.ValueType := vtString;
771 end else
772 if Command = 'int' then begin
773 Expect(Line, '');
774 Parameter := GetNextToken(Line);
775 if not Assigned(NewItem) then raise Exception.Create('Item not defined.' + GetLog);
776 NewItem.Name := Parameter;
777 NewItem.ValueType := vtInt;
778 end else
779 if Command = 'hex' then begin
780 Expect(Line, '');
781 Parameter := GetNextToken(Line);
782 if not Assigned(NewItem) then raise Exception.Create('Item not defined.' + GetLog);
783 NewItem.Name := Parameter;
784 NewItem.ValueType := vtHex;
785 end else
786 if Command = 'range' then begin
787 Expect(Line, '');
788 Parameter := GetNextToken(Line);
789 if not Assigned(NewItem) then raise Exception.Create('Item not defined.' + GetLog);
790 NewItem.RangeMin := Parameter;
791 Parameter := GetNextToken(Line);
792 NewItem.RangeMax := Parameter;
793 end else
794 if Command = 'prompt' then begin
795 Expect(Line, '');
796 Parameter := GetNextToken(Line);
797 if not Assigned(NewItem) then raise Exception.Create('Item not defined.' + GetLog);
798 NewItem.Name := Parameter;
799 end else
800 if Command = 'default' then begin
801 Expect(Line, '');
802 Parameter := GetNextToken(Line);
803 if not Assigned(NewItem) then raise Exception.Create('Item not defined.' + GetLog);
804 NewItem.DefaultValue := Parameter;
805 end else
806 if Command = 'depends' then begin
807 Expect(Line, '');
808 Parameter := GetNextToken(Line);
809 if Parameter = 'on' then begin
810 if not Assigned(NewItem) then raise Exception.Create('Item not defined.' + GetLog);
811 ParseExpression(NewItem, Line);
812 end;
813 end else
814 if Command = 'select' then begin
815 Expect(Line, '');
816 Parameter := GetNextToken(Line);
817 if not Assigned(NewItem) then raise Exception.Create('Item not defined.' + GetLog);
818 NewItem.Selects.Add(Parameter);
819 end else
820 if Command = '---' then begin
821 Command := GetNextToken(Line);
822 if Command = 'help' then begin
823 Command := GetNextToken(Line);
824 if Command = '---' then begin
825 State := stHelp;
826 HelpIndent := LineIndent;
827 end;
828 end else
829 if Command = '' then begin
830 Command := GetNextToken(Line);
831 if Command = 'help' then begin
832 Expect(Line, '');
833 Command := GetNextToken(Line);
834 if Command = '---' then begin
835 State := stHelp;
836 HelpIndent := LineIndent;
837 end;
838 end;
839 end;
840 end else
841 if (Command = 'help') or (Command = '---help---') then begin
842 State := stHelp;
843 HelpIndent := LineIndent;
844 end else
845 if Command = 'option' then begin
846 Expect(Line, '');
847 Parameter := GetNextToken(Line);
848 end else
849 if (Command = 'config') or (Command = 'menuconfig') then begin
850 Expect(Line, '');
851 Parameter := GetNextToken(Line);
852 State := stConfig;
853 NewItem := TMenuNode.Create;
854 NewItem.Parent := CurrentMenu;
855 NewItem.ID := Parameter;
856 for J := 0 to ConditionStack.Count - 1 do
857 if ConditionStack[J] <> '' then
858 begin
859 NewExp := TExpressionVar.Create;
860 NewExp.Name := ConditionStack[J];
861 NewItem.DependsOn.Items.Add(NewExp);
862 end;
863 CurrentMenu.Items.Add(NewItem);
864 if Command = 'menuconfig' then NewItem.MenuConfig := True;
865 end else
866 if (Command = 'comment') then begin
867 Expect(Line, '');
868 Parameter := GetNextToken(Line);
869 NewItem := TMenuNode.Create;
870 NewItem.Parent := CurrentMenu;
871 NewItem.Name := Parameter;
872 for J := 0 to ConditionStack.Count - 1 do
873 if ConditionStack[J] <> '' then begin
874 NewExp := TExpressionVar.Create;
875 NewExp.Name := ConditionStack[J];
876 NewItem.DependsOn.Items.Add(NewExp);
877 end;
878 NewItem.ValueType := vtComment;
879 CurrentMenu.Items.Add(NewItem);
880 end else
881 if Command = 'source' then begin
882 Expect(Line, '');
883 Parameter := GetNextToken(Line);
884 if Pos('$SRCARCH', Parameter) > 0 then
885 Parameter := StringReplace(Parameter, '$SRCARCH', Arch, [rfReplaceAll]);
886 Parameter := BaseDir + DirectorySeparator + Parameter;
887 if FileExistsUTF8(Parameter) then
888 ParseFile(Parameter)
889 else raise Exception.Create('Source file "' + Parameter + '" not found.' + GetLog);
890 end else
891 if Command = 'choice' then begin
892 Expect(Line, '');
893 Parameter := GetNextToken(Line);
894 NewMenu := TMenuNode.Create;
895 NewMenu.ValueType := vtChoice;
896 NewMenu.Name := Parameter;
897 NewMenu.Parent := CurrentMenu;
898 for J := 0 to ConditionStack.Count - 1 do
899 if ConditionStack[J] <> '' then begin
900 NewExp := TExpressionVar.Create;
901 NewExp.Name := ConditionStack[J];
902 NewMenu.DependsOn.Items.Add(NewExp);
903 end;
904 ConditionStack.AddObject('', nil);
905 NewItem := NewMenu;
906 CurrentMenu.Items.Add(NewMenu);
907 CurrentMenu := NewMenu;
908 end else
909 if command = 'endchoice' then begin
910 if Assigned(CurrentMenu.Parent) then begin
911 ConditionStack.Delete(ConditionStack.Count - 1);
912 CurrentMenu := CurrentMenu.Parent;
913 end else raise Exception.Create('Can''t change menu level up.' + GetLog);
914 end else
915 if Command = 'mainmenu' then begin
916 Expect(Line, '');
917 Parameter := GetNextToken(Line);
918 Parameter := StringReplace(Parameter, '$ARCH', Arch, [rfReplaceAll]);
919 Parameter := StringReplace(Parameter, '$KERNELVERSION', Version, [rfReplaceAll]);
920 TopNode.Name := Parameter;
921 end else
922 if Command = 'menu' then begin
923 Expect(Line, '');
924 Parameter := GetNextToken(Line);
925 Log('MENU ' + Parameter + ' IN ' + CurrentMenu.GetAbsoluteName);
926 NewMenu := TMenuNode.Create;
927 NewMenu.Name := Parameter;
928 NewMenu.Parent := CurrentMenu;
929 for J := 0 to ConditionStack.Count - 1 do
930 if ConditionStack[J] <> '' then begin
931 NewExp := TExpressionVar.Create;
932 NewExp.Name := ConditionStack[J];
933 NewMenu.DependsOn.Items.Add(NewExp);
934 end;
935 ConditionStack.AddObject('', nil);
936 NewItem := NewMenu;
937 CurrentMenu.Items.Add(NewMenu);
938 CurrentMenu := NewMenu;
939 end else
940 if command = 'endmenu' then begin
941 Log('ENDMENU ' + CurrentMenu.GetAbsoluteName);
942 if Assigned(CurrentMenu.Parent) then begin
943 ConditionStack.Delete(ConditionStack.Count - 1);
944 CurrentMenu := CurrentMenu.Parent;
945 end else raise Exception.Create('Can''t change menu level up.' + GetLog);
946 end else
947 if Command = 'if' then begin
948 Expect(Line, '');
949 ConditionStack.AddObject(GetNextToken(Line), NewItem);
950 end else
951 if Command = 'visible' then begin
952 //VisibleCondition := GetNextToken(Line);
953 end else
954 if Command = 'optional' then begin
955 NewItem.Optional := True;
956 end else
957 if Command = 'endif' then begin
958 ConditionStack.Delete(ConditionStack.Count - 1);
959 end else
960 raise Exception.Create('Unknown command "' + Command + '".' + GetLog);
961 end;
962 finally
963 Content.Free;
964 end;
965end;
966
967procedure TConfigMenu.ParseConfig;
968begin
969 ConditionStack.Clear;
970 TopNode.Free;
971 TopNode := TMenuNode.Create;
972 TopNode.Name := 'Root';
973 CurrentMenu := TopNode;
974 ParseFile(BaseDir + DirectorySeparator + 'Kconfig');
975end;
976
977procedure TConfigMenu.ParseMakeFile;
978var
979 Content: TStringList;
980 I: Integer;
981 Line: string;
982 Token: string;
983begin
984 try
985 Content := TStringList.Create;
986 Content.LoadFromFile(BaseDir + DirectorySeparator + 'Makefile');
987 for I := 0 to Content.Count - 1 do begin
988 Line := Trim(Content[I]);
989 Token := GetNextToken(Line);
990 if Token = 'VERSION' then begin
991 Line := Trim(Line);
992 Token := GetNextToken(Line);
993 if Token = '=' then
994 Version := Trim(Line);
995 end else
996 if Token = 'PATCHLEVEL' then begin
997 Line := Trim(Line);
998 Token := GetNextToken(Line);
999 if Token = '=' then
1000 Version := Version + '.' + Trim(Line);
1001 end else
1002 if Token = 'SUBLEVEL' then begin
1003 Line := Trim(Line);
1004 Token := GetNextToken(Line);
1005 if Token = '=' then
1006 Version := Version + '.' + Trim(Line);
1007 end;
1008 end;
1009 finally
1010 Content.Free;
1011 end;
1012end;
1013
1014procedure TConfigMenu.OptionListNames(List: TStringList);
1015var
1016 I: Integer;
1017 Node: TMenuNode;
1018begin
1019 for I := 0 to List.Count - 1 do begin
1020 Node := TopNode.FindNode(List[I]);
1021 if Assigned(Node) then
1022 List[I] := '"' + Node.GetAbsoluteName + '";' + List[I];
1023 end;
1024end;
1025
1026procedure TConfigMenu.PrepareMoveList(var List: TObjectMoves);
1027var
1028 Lookup: TStringList;
1029begin
1030 try
1031 Lookup := TStringList.Create;
1032 Lookup.OwnsObjects := False;
1033 TopNode.GetNodes(Lookup);
1034 Lookup.Sorted := True;
1035 TopNode.PrepareMoveList(Lookup, List);
1036 finally
1037 Lookup.Free;
1038 end;
1039end;
1040
1041procedure TConfigMenu.CompareStringLists(List1, List2: TStringList;
1042 Missing1, Missing2: TStrings);
1043var
1044 I: Integer;
1045 J: Integer;
1046begin
1047 List1.Sort;
1048 List2.Sort;
1049 I := 0;
1050 J := 0;
1051 while (I < List1.Count) and (J < List2.Count) do
1052 begin
1053 if List1[I] < List2[J] then
1054 begin
1055 Missing2.Add(List1[I]);
1056 Inc(I);
1057 end
1058 else if List1[I] > List2[J] then
1059 begin
1060 Missing1.Add(List2[J]);
1061 Inc(J);
1062 end
1063 else
1064 begin
1065 Inc(I);
1066 Inc(J);
1067 end;
1068 end;
1069 for I := I to List1.Count - 1 do
1070 Missing2.Add(List1[I]);
1071 for J := J to List2.Count - 1 do
1072 Missing1.Add(List2[J]);
1073end;
1074
1075procedure TConfigMenu.LoadFromDir(Dir: string);
1076var
1077 Moves: TObjectMoves;
1078 I: Integer;
1079begin
1080 BaseDir := Dir;
1081 ParseMakeFile;
1082 ParseConfig;
1083
1084 SetLength(Moves, 0);
1085 PrepareMoveList(Moves);
1086 for I := 0 to Length(Moves) - 1 do
1087 with Moves[I] do begin
1088 TMenuNode(Source).Parent.Items.OwnsObjects := False;
1089 TMenuNode(Source).Parent.Items.Remove(Source);
1090 TMenuNode(Source).Parent.Items.OwnsObjects := True;
1091 TMenuNode(Source).Parent := TMenuNode(NewParent);
1092 TMenuNode(NewParent).Items.Add(Source);
1093 end;
1094end;
1095
1096constructor TConfigMenu.Create;
1097begin
1098 ConditionStack := TStringList.Create;
1099 Self.FArch := 'x86';
1100end;
1101
1102destructor TConfigMenu.Destroy;
1103begin
1104 ConditionStack.Free;
1105end;
1106
1107end.
1108
1109
Note: See TracBrowser for help on using the repository browser.