2

I'm using Inno Setup tool to install some program and its associated macros this way:

[Files]
Source: "..\macros\*.js"; DestDir: "{app}\macros"; Flags: ignoreversion

Now what I would like to do is to let the user choose for a subset of macros instead of installing them all. For instance I'd like to show a custom checklistbox page during installation where the user can select for the macros he'd like to install (with extra 'select all', 'select none' shortcut buttons would be great).

Do you know any similar case/example Inno Setup-script I can inspire from ?

NB1: I have no idea of how many macros there is in the ..\macros\ folder, neither the name of them all, so I can't list them all one-by-one in the [Files] section.

NB2: I have read this thread but this not what I'd really like (would need to update the .iss on any change in the macros folder which is not a smart solution) : How can I add a CheckBox for optional files during install? (innosetup)

Martin Prikryl
  • 188,800
  • 56
  • 490
  • 992
CitizenInsane
  • 4,755
  • 1
  • 25
  • 56
  • 1
    See [Inno Setup: Dynamically add a component for all files in a folder and its subfolders](https://stackoverflow.com/q/45059158/850848) – Your code can actually be simpler, as you do not need it to recurse to subdirectories. – Martin Prikryl Nov 13 '19 at 15:39
  • @MartinPrikryl sounds great, so far I have compiler error replacing emit with my own folder `#emit ProcessFolder("..\macros", "")` ... need to further look into it ... the error message is "Parameter "name" contains invalid characters" – CitizenInsane Nov 13 '19 at 15:57
  • Try using an absolute path. If you do not make it working, please post [mcve]. – Martin Prikryl Nov 13 '19 at 16:09
  • @MartinPrikryl I think I got it ... The error does not come from absolute/relative path but from the name of my macros which contains brackets `[ ]` ... See the picture here: https://imgur.com/a/QEZdcWS ... if folder contains only `macro1.js` everything works fine but adding `[MyTool] - macro2.js` creates the issue. – CitizenInsane Nov 13 '19 at 16:23
  • OK, and do you need to make it working with such files? Or can you just rename them? – Martin Prikryl Nov 13 '19 at 16:29
  • @MartinPrikryl Yes I need to work with such names ... i think it is just adding extra `"` in the preprocessing macros, right ? I will try to modify directivres and let you know – CitizenInsane Nov 13 '19 at 16:46
  • Quotes won't help. You need to remove (or replace) the `[ ]` from the component name. – Martin Prikryl Nov 13 '19 at 16:49
  • See how `.` is already stripped: `Local[1] = StringChange(Local[0], ".", "")`, extended it to `Local[1] = StringChange(StringChange(StringChange(Local[0], ".", ""), "[", ""), "]", "")` – Martin Prikryl Nov 13 '19 at 16:59
  • @MartinPrikryl `StringChange(StringChange(StringChange(StringChange(StringChange(Local[0], ".", ""), "[", ""), "]", ""), "-", ""), " ", "")` did it :) ... I will further debug preprocessed script with https://stackoverflow.com/a/3328474/684399 to see how exactly it is extended. Thanks a lot for your support. – CitizenInsane Nov 13 '19 at 17:20

2 Answers2

2

Solution using preprocessor directives and code emission as proposed by @MartinPrikryl in question comments and described in details in this other thread is a very nice one and I would definitely recommend it.

Here is anyhow some alternative using Pascal scripting only:

  1. First I let the Setup installing all the macros, but I watch for them using a AfterInstall handler in the [File] section:

    [Files]
    Source: ".\macros\*.txt"; DestDir: "{app}\macros"; Flags: ignoreversion; AfterInstall: AfterInstallEachMacro;
    
  2. Handling procedure AfterInstallEachMacro just records all macros in global variables of the [Code] section and populates some extra wizard page that will be displayed after installation is completed:

    procedure AfterInstallEachMacro();
    var 
      filename : String; 
    begin
    
      filename := CurrentFileName;
      filename := ExpandConstant(filename);
      GMacrosFullFilenames.Add(filename);
    
      filename := ExtractFileName(filename);  
      GMacrosSelectectionPage.Add(filename);
      GMacrosSelectectionPage.Values[GMacrosTotalCount] := True;
      GMacrosTotalCount := GMacrosTotalCount + 1;
    
    end;
    
  3. Finally, once leaving that last wizard page, I simply delete macros the user did not selected:

    function NextButtonClick(CurPageID: Integer): Boolean;
      var index: Integer;
    begin
      Result := true; 
    
      {* When leaving page for macro selection *}
      if (CurPageID = GMacrosSelectectionPage.ID) then begin
    
        {* Delete macros the user do not want *}
        for index := 0 to (GMacrosTotalCount - 1) do 
        begin
          if (not(GMacrosSelectectionPage.Values[index])) then begin
            DeleteFile(GMacrosFullFilenames[index]);
          end;
        end;
    
      end;
    end;
    

This approach is maybe less elegant or can be further improved but was better suited for my particular case than solution proposed by @MartinPrikryl.

Full innoscript is hosted here. Here is how it looks like:

Choosing macros to install

Martin Prikryl
  • 188,800
  • 56
  • 490
  • 992
CitizenInsane
  • 4,755
  • 1
  • 25
  • 56
  • Assuming that `GMacrosFullFilenames` is `TStrings`/`TStringList`, you can use `GMacrosFullFilenames.Count` and you do not need `GMacrosTotalCount`. – Martin Prikryl Nov 15 '19 at 08:41
  • @MartinPrikryl Indeed you're right... Major problem with this solution is that is comes after installation (no go back button) and is somehow incomplete so far (no select all/none)... Your solution is more elegant (though in my more complex case in practice, I could not see how to turns these macros files into components or tasks without deeply changing installation paradigm I already had). Thanks a lot for your help and ideas. – CitizenInsane Nov 15 '19 at 10:55
1

If you want to define some "groups" of macros in advance (rather than having them be individually selectable), then you can simply arrange your source files into those groups, and then set up components accordingly:

[Components]
Name: macroX; Description: "Macros for X"
Name: macroY; Description: "Macros for Y"

[Files]
Source: "..\macros\macroX\*.js"; DestDir: "{app}\macros"; Components: macroX; Flags: ignoreversion
Source: "..\macros\macroY\*.js"; DestDir: "{app}\macros"; Components: macroY; Flags: ignoreversion

It would be possible to make individual selection within these groups as well, but you'd have to name the files individually:

[Components]
Name: macroX\macro1; Description: "Macro1"
[Files]
Source: "..\macros\macroX\macro1.js"; DestDir: "{app}\macros"; Components: macroX\macro1; Flags: ignoreversion

It's possible to use ISPP to generate code like the above using wildcards, but that is left as an exercise for the reader.


Finally, you can use [Types] to provide select-all / select-none functionality:

[Setup]
AlwaysShowComponentsList=yes
[Types]
Name: all; Description: "All macros"
Name: none; Description: "No macros"
Name: custom; Description: "Custom selection"; Flags: iscustom

And then put Types: all on all of your [Components] entries.

Miral
  • 12,637
  • 4
  • 53
  • 93
  • Effective setup i have is a little more complex than example one in the question to rearrange like this right now but yes with ISPP as showed by @MartinPrikril this would be elegant way to organise things. Thanks – CitizenInsane Nov 18 '19 at 08:22