1
{ Main form for the lazarus package manager
3
Copyright (C) 2011 Darius Blaszyk
5
This library is free software; you can redistribute it and/or modify it
6
under the terms of the GNU Library General Public License as published by
7
the Free Software Foundation; either version 2 of the License, or (at your
8
option) any later version with the following modification:
10
As a special exception, the copyright holders of this library give you
11
permission to link this library with independent modules to produce an
12
executable, regardless of the license terms of these independent modules,and
13
to copy and distribute the resulting executable under terms of your choice,
14
provided that you also meet, for each linked independent module, the terms
15
and conditions of the license of that module. An independent module is a
16
module which is not derived from or based on this library. If you modify
17
this library, you may extend this exception to your version of the library,
18
but you are not obligated to do so. If you do not wish to do so, delete this
19
exception statement from your version.
21
This program is distributed in the hope that it will be useful, but WITHOUT
22
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
23
FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License
26
You should have received a copy of the GNU Library General Public License
27
along with this library; if not, write to the Free Software Foundation,
28
Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
34
{$IFDEF VER2_4}{$ERROR This package requires at least fpc 2.5.1}{$ENDIF}
39
Classes, SysUtils, Forms, Controls, StdCtrls, ComCtrls, ExtCtrls, Buttons,
40
Menus, CheckLst, Dialogs, fppkg_const,
41
fppkg_optionsfrm, fppkg_details,
43
{$IFDEF LazarusIDEPackage}
44
IDEIntf, PackageIntf, IDECommands, contnrs, fppkg_lpk,
46
// Repository handler objects
48
pkgmessages, pkgglobals, pkgoptions, pkgrepos, laz_pkgrepos,
49
// Package Handler components
50
pkghandler, laz_pkghandler, laz_pkgcommands, pkgcommands,
55
TFppkgConfigOptions = record
61
TFppkgForm = class(TForm)
62
CategoriesLabel: TLabel;
63
CategoryCheckListBox: TCheckListBox;
66
miCleanMessages: TMenuItem;
70
miShowDetails: TMenuItem;
71
miSeparator: TMenuItem;
72
PackageListView: TListView;
73
PackageSupportImages: TImageList;
79
miUnselect: TMenuItem;
81
PackagePopupMenu: TPopupMenu;
85
StatusBar1: TStatusBar;
86
SupportCheckGroup: TCheckGroup;
87
ToolbarImages: TImageList;
90
SearchButton: TSpeedButton;
92
BuildButton: TToolButton;
93
CompileButton: TToolButton;
94
FixBrokenButton: TToolButton;
95
UpdateButton: TToolButton;
96
InstallButton: TToolButton;
97
CleanButton: TToolButton;
98
ArchiveButton: TToolButton;
99
DownloadButton: TToolButton;
100
VertSplitter: TSplitter;
101
procedure ArchiveButtonClick(Sender: TObject);
102
procedure BuildButtonClick(Sender: TObject);
103
procedure CategoryCheckListBoxClickCheck(Sender: TObject);
104
procedure CleanButtonClick(Sender: TObject);
105
procedure CompileButtonClick(Sender: TObject);
106
procedure DownloadButtonClick(Sender: TObject);
107
procedure FixBrokenButtonClick(Sender: TObject);
108
procedure FormCreate(Sender: TObject);
109
procedure FormDestroy(Sender: TObject);
110
procedure InstallButtonClick(Sender: TObject);
111
procedure MenuItem4Click(Sender: TObject);
112
procedure miClearMemoClick(Sender: TObject);
113
procedure miCleanMessagesClick(Sender: TObject);
114
procedure PackageListViewDblClick(Sender: TObject);
115
procedure MenuItem2Click(Sender: TObject);
116
procedure miExitClick(Sender: TObject);
117
procedure miSelectClick(Sender: TObject);
118
procedure miUnselectClick(Sender: TObject);
119
procedure SearchButtonClick(Sender: TObject);
120
procedure SearchEditKeyUp(Sender: TObject; var Key: word; Shift: TShiftState);
121
procedure SupportCheckGroupItemClick(Sender: TObject; Index: integer);
122
procedure UpdateButtonClick(Sender: TObject);
124
{ private declarations }
125
SearchPhrases: TStrings;
127
function FindSearchPhrase(pkg: TLazPackageData): boolean;
128
function FindCategory(pkg: TLazPackageData): boolean;
129
function FindSupport(pkg: TLazPackageData): boolean;
130
procedure GetSelectedPackages(var s: TStrings);
132
procedure MaybeCreateLocalDirs;
133
procedure DoRun(cfg: TFppkgConfigOptions; ParaAction: string; ParaPackages: TStrings);
134
procedure LoadCompilerDefaults;
136
procedure UpdatePackageListView;
137
procedure ListPackages;
139
{ public declarations }
143
FppkgForm: TFppkgForm;
144
FppkgCfg: TFppkgConfigOptions;
151
Masks, fppkg_aboutfrm;
153
procedure LazLog(Level: TLogLevel; const Msg: string);
157
if not(Level in LogLevels) then
173
if Assigned(FppkgForm) then
174
FppkgForm.OutputMemo.Lines.Add(DateTimeToStr(Now) + ' ' + Prefix + ' ' + Msg);
177
procedure LazError(const Msg: String);
184
procedure TFppkgForm.FixBrokenButtonClick(Sender: TObject);
188
s := TStringList.Create;
189
DoRun(FppkgCfg, 'laz_fixbroken', s);
191
UpdatePackageListView;
195
procedure TFppkgForm.CleanButtonClick(Sender: TObject);
199
s := TStringList.Create;
201
GetSelectedPackages(s);
204
Error(SErrNoPackageSpecified)
206
DoRun(FppkgCfg, 'clean', s);
211
procedure TFppkgForm.CompileButtonClick(Sender: TObject);
215
s := TStringList.Create;
217
GetSelectedPackages(s);
220
Error(SErrNoPackageSpecified)
222
DoRun(FppkgCfg, 'compile', s);
227
procedure TFppkgForm.DownloadButtonClick(Sender: TObject);
231
s := TStringList.Create;
233
GetSelectedPackages(s);
236
Error(SErrNoPackageSpecified)
238
DoRun(FppkgCfg, 'download', s);
243
procedure TFppkgForm.ArchiveButtonClick(Sender: TObject);
247
s := TStringList.Create;
249
GetSelectedPackages(s);
252
Error(SErrNoPackageSpecified)
254
DoRun(FppkgCfg, 'archive', s);
259
procedure TFppkgForm.BuildButtonClick(Sender: TObject);
263
s := TStringList.Create;
265
GetSelectedPackages(s);
268
Error(SErrNoPackageSpecified)
270
DoRun(FppkgCfg, 'build', s);
275
procedure TFppkgForm.CategoryCheckListBoxClickCheck(Sender: TObject);
277
UpdatePackageListView;
280
procedure TFppkgForm.FormCreate(Sender: TObject);
282
//setup log callback function
283
LogHandler := @LazLog;
285
//setup error callback function
286
ErrorHandler := @LazError;
287
SetDefaultRepositoryClass(TLazFPRepository);
290
Caption := rsFreePascalPackageManagerForLazarus;
292
SupportCheckGroup.Checked[0] := True;
293
SupportCheckGroup.Checked[1] := True;
294
SupportCheckGroup.Checked[2] := True;
296
SearchPhrases := TStringList.Create;
297
SearchPhrases.Delimiter := ' ';
301
UpdatePackageListView;
304
procedure TFppkgForm.FormDestroy(Sender: TObject);
306
FreeAndNil(AvailableRepository);
310
procedure TFppkgForm.InstallButtonClick(Sender: TObject);
313
{$IFDEF LazarusIDEPackage}
315
RebuildLazarus: boolean;
316
PkgFlags: TPkgInstallInIDEFlags;
317
APackage: TIDEPackage;
318
InstPackages: TObjectList;
323
s := TStringList.Create;
325
GetSelectedPackages(s);
328
Error(SErrNoPackageSpecified)
331
DoRun(FppkgCfg, 'install', s);
333
UpdatePackageListView;
335
{$IFDEF LazarusIDEPackage}
336
RebuildLazarus := False;
337
InstPackages:=TObjectList.create;
339
PkgFlags := [piiifQuiet];
340
for i:=0 to s.Count-1 do
342
P := InstalledRepository.FindPackage(s.Strings[i]) as TLazFPPackage;
343
if P.HasLazarusPackageFiles then
344
for j := 0 to p.LazarusPackageFiles.Count-1 do
346
LPKFile := P.LazarusPackageFiles.Strings[j];
348
//make sure to determine if the IDE needs to be rebuilt
349
if LPKStatus(LPKFile) in [lpDesigntime, lpBoth] then
351
RebuildLazarus := True;
352
PkgFlags := PkgFlags + [piiifRebuildIDE];
355
//add LPK file to IDE
356
{$note what's the modal result doing here?}
358
PackageEditingInterface.DoOpenPackageFile(LPKFile,[pofRevert,pofDoNotOpenEditor],true);
360
for k := 0 to PackageEditingInterface.GetPackageCount-1 do
361
if PackageEditingInterface.GetPackages(k).Filename = LPKFile then
363
APackage := PackageEditingInterface.GetPackages(k);
366
if not assigned(APackage) then
367
raise exception.create('Failed to find just installed package. Something went wrong.');
368
InstPackages.Add(APackage);
372
if InstPackages.Count>0 then
373
PackageEditingInterface.InstallPackages(InstPackages,PkgFlags);
377
if RebuildLazarus then
378
ExecuteIDECommand(Self, ecBuildLazarus);
384
procedure TFppkgForm.MenuItem4Click(Sender: TObject);
386
if not Assigned(FppkgAboutForm) then
387
FppkgAboutForm := TFppkgAboutForm.Create(Self);
389
FppkgAboutForm.ShowModal;
390
FreeAndNil(FppkgAboutForm);
393
procedure TFppkgForm.miClearMemoClick(Sender: TObject);
398
procedure TFppkgForm.miCleanMessagesClick(Sender: TObject);
403
procedure TFppkgForm.PackageListViewDblClick(Sender: TObject);
405
//only for selected items show details
406
if not Assigned(PackageListView.Selected) then
409
if not Assigned(PkgDetailsForm) then
410
PkgDetailsForm := TPkgDetailsForm.Create(Self);
412
PkgDetailsForm.PackageName := PackageListView.Selected.Caption;
413
PkgDetailsForm.ShowModal;
415
FreeAndNil(PkgDetailsForm);
418
procedure TFppkgForm.MenuItem2Click(Sender: TObject);
420
if not Assigned(OptionsForm) then
421
OptionsForm := TOptionsForm.Create(Self);
423
OptionsForm.ShowModal;
425
//to be sure setup the view again
426
UpdatePackageListView;
429
procedure TFppkgForm.miExitClick(Sender: TObject);
434
procedure TFppkgForm.miSelectClick(Sender: TObject);
438
for i := 0 to PackageListView.Items.Count - 1 do
439
if PackageListView.Items[i].Selected then
440
PackageListView.Items[i].Checked := True;
443
procedure TFppkgForm.miUnselectClick(Sender: TObject);
447
for i := 0 to PackageListView.Items.Count - 1 do
448
if PackageListView.Items[i].Selected then
449
PackageListView.Items[i].Checked := False;
452
procedure TFppkgForm.SearchButtonClick(Sender: TObject);
454
SearchPhrases.DelimitedText := SearchEdit.Text;
455
UpdatePackageListView;
458
procedure TFppkgForm.SearchEditKeyUp(Sender: TObject; var Key: word;
463
SearchPhrases.DelimitedText := SearchEdit.Text;
464
UpdatePackageListView;
468
procedure TFppkgForm.SupportCheckGroupItemClick(Sender: TObject; Index: integer);
470
UpdatePackageListView;
473
procedure TFppkgForm.UpdateButtonClick(Sender: TObject);
477
s := TStringList.Create;
478
DoRun(FppkgCfg, 'update', s);
479
UpdatePackageListView;
483
procedure TFppkgForm.MaybeCreateLocalDirs;
485
ForceDirectories(GlobalOptions.BuildDir);
486
ForceDirectories(GlobalOptions.ArchivesDir);
487
ForceDirectories(GlobalOptions.CompilerConfigDir);
490
function TFppkgForm.FindSearchPhrase(pkg: TLazPackageData): boolean;
497
if SearchPhrases.Count = 0 then
500
for i := 0 to SearchPhrases.Count - 1 do
502
searchmask := LowerCase('*' + SearchPhrases[i] + '*');
504
if MatchesMask(LowerCase(pkg.Description), searchmask) or
505
MatchesMask(LowerCase(pkg.Category), searchmask) or
506
MatchesMask(LowerCase(pkg.Keywords), searchmask) or
507
MatchesMask(LowerCase(pkg.Name), searchmask) then
515
function TFppkgForm.FindCategory(pkg: TLazPackageData): boolean;
522
for i := 0 to CategoryCheckListBox.Count - 1 do
524
if CategoryCheckListBox.Checked[i] then
526
//determine the searchmask
527
if CategoryCheckListBox.Items[i] = 'All' then
530
if CategoryCheckListBox.Items[i] = 'Unknown' then
533
searchmask := CategoryCheckListBox.Items[i];
535
if MatchesMask(pkg.Category, searchmask) then
544
function TFppkgForm.FindSupport(pkg: TLazPackageData): boolean;
549
Result := Result or (SupportCheckGroup.Checked[0] and (pkg.Category = 'FPC'));
552
Result := Result or (SupportCheckGroup.Checked[1] and (pkg.Category = 'Lazarus'));
555
Result := Result or (SupportCheckGroup.Checked[2] and
556
((pkg.Category <> 'FPC') and (pkg.Category <> 'Lazarus')));
559
procedure TFppkgForm.GetSelectedPackages(var s: TStrings);
563
for i := 0 to PackageListView.Items.Count - 1 do
564
if PackageListView.Items[i].Checked then
565
s.Add(PackageListView.Items[i].Caption);
568
procedure TFppkgForm.LoadCompilerDefaults;
572
// Load default compiler config
573
S := GlobalOptions.CompilerConfigDir + GlobalOptions.CompilerConfig;
574
CompilerOptions.UpdateLocalRepositoryOption;
575
if FileExists(S) then
577
pkgglobals.Log(vlDebug, SLogLoadingCompilerConfig, [S]);
578
CompilerOptions.LoadCompilerFromFile(S);
582
// Generate a default configuration if it doesn't exists
583
if GlobalOptions.CompilerConfig = 'default' then
585
pkgglobals.Log(vlDebug, SLogGeneratingCompilerConfig, [S]);
586
CompilerOptions.InitCompilerDefaults;
587
CompilerOptions.SaveCompilerToFile(S);
588
if CompilerOptions.SaveInifileChanges then
589
CompilerOptions.SaveCompilerToFile(S);
592
Error(SErrMissingCompilerConfig, [S]);
594
// Log compiler configuration
595
CompilerOptions.LogValues(vlDebug, '');
596
// Load FPMake compiler config, this is normally the same config as above
597
S := GlobalOptions.CompilerConfigDir + GlobalOptions.FPMakeCompilerConfig;
598
FPMakeCompilerOptions.UpdateLocalRepositoryOption;
599
if FileExists(S) then
601
pkgglobals.Log(vlDebug, SLogLoadingFPMakeCompilerConfig, [S]);
602
FPMakeCompilerOptions.LoadCompilerFromFile(S);
603
if FPMakeCompilerOptions.SaveInifileChanges then
604
FPMakeCompilerOptions.SaveCompilerToFile(S);
607
Error(SErrMissingCompilerConfig, [S]);
608
// Log compiler configuration
609
FPMakeCompilerOptions.LogValues(vlDebug, 'fpmake-building ');
612
procedure TFppkgForm.DoRun(cfg: TFppkgConfigOptions; ParaAction: string;
613
ParaPackages: TStrings);
615
ActionPackage: TFPPackage;
620
OldCurrDir := GetCurrentDir;
622
LoadGlobalDefaults(cfg.ConfigFile);
623
//ProcessCommandLine(true);
625
// Scan is special, it doesn't need a valid local setup
626
if (ParaAction = 'laz_scan') then
628
RebuildRemoteRepository;
629
ListRemoteRepository;
630
SaveRemoteRepository;
634
MaybeCreateLocalDirs;
635
if not GlobalOptions.SkipConfigurationFiles then
639
FPMakeCompilerOptions.InitCompilerDefaults;
640
CompilerOptions.InitCompilerDefaults;
643
// The command-line is parsed for the second time, to make it possible
644
// to override the values in the compiler-configuration file. (like prefix)
645
//ProcessCommandLine(false);
647
// If CompilerVersion, CompilerOS or CompilerCPU is still empty, use the
648
// compiler-executable to get them
649
FPMakeCompilerOptions.CheckCompilerValues;
650
CompilerOptions.CheckCompilerValues;
652
LoadLocalAvailableMirrors;
654
// Load local repository, update first if this is a new installation
655
// errors will only be reported as warning. The user can be bootstrapping
656
// and do an update later
657
if not FileExists(GlobalOptions.LocalPackagesFile) then
660
laz_pkghandler.Laz_ExecuteAction('', 'laz_update');
663
pkgglobals.Log(vlWarning, E.Message);
666
LoadLocalAvailableRepository;
667
FindInstalledPackages(FPMakeCompilerOptions, True);
668
CheckFPMakeDependencies;
669
// We only need to reload the status when we use a different
670
// configuration for compiling fpmake
671
if GlobalOptions.CompilerConfig <> GlobalOptions.FPMakeCompilerConfig then
672
FindInstalledPackages(CompilerOptions, True);
674
// Check for broken dependencies
675
if not GlobalOptions.AllowBroken and
676
(((ParaAction = 'laz_fixbroken') and (ParaPackages.Count > 0)) or
677
(ParaAction = 'laz_compile') or (ParaAction = 'laz_build') or
678
(ParaAction = 'laz_install') or (ParaAction = 'laz_archive')) then
680
pkgglobals.Log(vlDebug, SLogCheckBrokenDependenvies);
681
SL := TStringList.Create;
682
if FindBrokenPackages(SL) then
683
Error(SErrBrokenPackagesFound);
687
if ParaPackages.Count = 0 then
689
ActionPackage := AvailableRepository.AddPackage(CurrentDirPackageName);
690
laz_pkghandler.Laz_ExecuteAction(CurrentDirPackageName, ParaAction);
695
for i := 0 to ParaPackages.Count - 1 do
697
if sametext(ExtractFileExt(ParaPackages[i]), '.zip') and
698
FileExists(ParaPackages[i]) then
700
ActionPackage := AvailableRepository.AddPackage(CmdLinePackageName);
701
ActionPackage.LocalFileName := ExpandFileName(ParaPackages[i]);
702
laz_pkghandler.Laz_ExecuteAction(CmdLinePackageName, ParaAction);
706
pkgglobals.Log(vlDebug, SLogCommandLineAction,['[' + ParaPackages[i] + ']', ParaAction]);
707
laz_pkghandler.Laz_ExecuteAction(ParaPackages[i], ParaAction);
712
// Recompile all packages dependent on this package
713
if (ParaAction = 'install') and not GlobalOptions.SkipFixBrokenAfterInstall then
714
laz_pkghandler.Laz_ExecuteAction('', 'fixbroken');
719
Error(SErrException);
724
SetCurrentDir(OldCurrDir);
727
function PkgColumnValue(AName: string; pkg: TLazPackageData): string;
729
if AName = 'Name' then
731
if AName = 'Installed' then
732
Result := pkg.InstalledVersion;
733
if AName = 'Available' then
734
Result := pkg.AvialableVersion;
735
if AName = 'Description' then
736
Result := pkg.Description;
737
if AName = 'State' then
739
if AName = 'Keywords' then
740
Result := pkg.Keywords;
741
if AName = 'Category' then
742
Result := pkg.Category;
743
if AName = 'Support' then
744
Result := pkg.Support;
745
if AName = 'Author' then
746
Result := pkg.Author;
747
if AName = 'License' then
748
Result := pkg.License;
749
if AName = 'HomepageURL' then
750
Result := pkg.HomepageURL;
751
if AName = 'DownloadURL' then
752
Result := pkg.DownloadURL;
753
if AName = 'FileName' then
754
Result := pkg.FileName;
755
if AName = 'Email' then
759
if AName = 'CPU' then
763
procedure TFppkgForm.UpdatePackageListView;
767
pkg: TLazPackageData;
771
//setup the package listview
772
PackageListView.BeginUpdate;
775
PackageListView.Columns.Clear;
776
for c := 0 to LazPkgOptions.PkgColumnCount - 1 do
777
if LazPkgOptions.PkgColumns[c].Visible then
779
col := PackageListView.Columns.Add;
780
col.Caption := LazPkgOptions.PkgColumns[c].Name;
781
col.AutoSize := True;
784
PackageListView.Clear;
786
for i := 0 to Laz_Packages.Count - 1 do
788
pkg := Laz_Packages.PkgData[i];
790
if FindSearchPhrase(pkg) and FindCategory(pkg) and FindSupport(pkg) then
792
li := PackageListView.Items.Add;
795
for c := 0 to LazPkgOptions.PkgColumnCount - 1 do
797
if LazPkgOptions.PkgColumns[c].Visible then
800
li.Caption := PkgColumnValue(LazPkgOptions.PkgColumns[c].Name, pkg);
804
li.SubItems.Add(PkgColumnValue(LazPkgOptions.PkgColumns[c].Name, pkg));
807
//add images to supported packages
808
if LowerCase(pkg.Support) = 'fpc' then
809
li.ImageIndex := FPC_SUPPORTED
811
if LowerCase(pkg.Support) = 'lazarus' then
812
li.ImageIndex := LAZARUS_SUPPORTED
814
li.ImageIndex := COMMUNITY_SUPPORTED;
818
PackageListView.EndUpdate;
821
procedure TFppkgForm.ListPackages;
825
pkg: TLazPackageData;
828
//update the package list
829
s := TStringList.Create;
830
DoRun(FppkgCfg, 'laz_list', s);
833
//setup the categories listview
834
CategoryCheckListBox.Clear;
835
CategoryCheckListBox.Items.Add('All');
836
for i := 0 to Laz_Packages.Count - 1 do
838
pkg := Laz_Packages.PkgData[i];
840
if pkg.Category = '' then
845
if CategoryCheckListBox.Items.IndexOf(cat) = -1 then
846
CategoryCheckListBox.Items.Add(cat);
849
//check all the items
850
for i := 0 to CategoryCheckListBox.Count - 1 do
851
CategoryCheckListBox.Checked[i] := True;