How to recursivelly include folder/files using a wildcard in Inno Setup - inno-setup

I want to include language resource files from our build into our installers. The language resource files all have the same name, but reside in different sub-folders (one per locale), like this:
\Release
\bin
\es-MX
Localization.resources.dll
\fr-CA
Localization.resources.dll
etc.
In my [Files] section, I thought perhaps I might be able to do this (note the position of the asterisk):
Source: "..\\source\\Libraries\\Localization\\bin\\Release\\*\\Localization.resources.dll"; \
DestDir: "{app}\\MyApp"; Flags: ignoreversion recursesubdirs
Unfortunately, Inno Setup blows up, complaining that it can't find any files:
Compiler Error!
Line 129: No files found matching "C:\Development\HT\Installers\..\source\Libraries\Localization\bin\Release\*\Localization.resources.dll"
I would like Inno Setup to look for any sub-folder (hence the *) containing a file named Localization.resources.dll and upon installation, create a language directory with the same name (based on what is found via the wildcard) and copy the file to that folder, doing so for each folder that matches the criteria.
Essentially, I want to end up with this:
..
\MyApp
\es-MX
Localization.resources.dll
\fr-CA
Localization.resources.dll
In case it isn't obvious, I would prefer not to explicitly add the source and destination folder names, because we will be adding more languages/locales in the future, and I would like Inno Setup to automatically pick up any new language folders/files we create without having to change the installer source.
Is this possible?

Just use the recursesubdirs flag with a root path to a tree and the Localization.resources.dll filename. It will automatically do what you want: find all Localization.resources.dll files in the tree and install them to their respective subfolders:
Source: "..\source\Libraries\Localization\bin\Release\Localization.resources.dll"; \
DestDir: "{app}\MyApp"; Flags: ignoreversion recursesubdirs
As documented (emphasis mine):
recursesubdirs
Instructs the compiler or Setup to also search for the Source filename/wildcard in subdirectories under the Source directory.
Other possible approaches:
Generate the Files section using a preprocessor.
For a similar tasks, see:
Inno Setup - Recurse sub directories without creating those same sub directories
Generating Inno Setup file flags programmatically
Inno Setup: Dynamically add a component for all files in a folder and its subfolders
Generate the Files section using an external scripting language (with better functionality then Inno Setup preprocessor) and invoke it using the Exec preprocessor function. E.g. using PowerShell.

Related

Inno setup create folder for image files

I would like to create a folder on C drive with Inno Setup C:\A A Card Images
and I am assuming I can use the [Files]Source:"Path to Images" DestDir: "{app}"; Flags: ignoreversion
[Dirs]
Name: "C:\A A Card Images" This is what I have tried but it tells me File exists
So it seems obvious to me that I need to create the folder before I try to add Image files to the folder
So do I use [Dirs] or [Files] to create the folder?
And using DestDir: "{app}"; Flags: ignoreversion to load the Images into the folder seems wrong
Because I do not want the Images in the DestDir I want them in C:\A A Card Images
To create a new directory during the install, use the [Dirs] section. The Name is used to specify the location. Using "{app}\foldername" means to create the directory below the application's directory. To create it somewhere else, specify the full path to the directory.
[Dirs]
Name: "C:\Whatever"
Use the [Files] section to indicate the files you want to install, and the location of those files. You use DestDir to specify that location.
Note that users may not be pleased if you create directories and install files in non-standard locations. There are valid reasons that those standard locations exist. I personally don't like it when applications clutter my drive in places they shouldn't be using.

Inno Setup Compiler "Cannot find the path specified" error with long paths

I am using a .iss script to build an exe file inside Inno Setup Compiler. I need to package some node_modules into this application so I have a line under [Files] which looks like this:
Source: "{#SourcePath}Encore.Warehouse.UI\bin\Warehouse_Release\warehouse\*"; \
DestDir: "{app}\warehouse"; Flags: ignoreversion recursesubdirs createallsubdirs
When I compile, I receive this error:
Here is the compiler output:
So, it appears to be running fine up until it aborted. My initial thought was that the browser.js doesn't exist but after double checking, this is not the case. Also, I'm using a wildcard in the source path so the compiler knows the file exists, but it seems to be having trouble compressing it.
One other thing that could be causing the issue is the file path length. Node modules usually end up having ridiculous file path lengths due to nested dependencies. In this case, the path length is 260. Assuming this is what's causing the problem, is there any way to get around it?
It's definitely due to a long path. Normally Windows applications cannot process paths longer than MAX_PATH (260 characters).
See Naming Files, Paths, and Namespaces in Microsoft documentation.
A common workaround is prefixing the path with \\?\ (again see the Microsoft article above). The prefix can be used for absolute paths only. But Inno Setup compiler chokes on that with the Source attribute. It looks for : and it accepts only path that either have a drive letter only before the : or that use compiler: or userdocs: prefixes.
You can hack that by using an UNC path with a volume ID (hence no colon).
Use the mountvol command to find the UNC path for your source drive.
And then you will have the same problem with a long path with the DestDir attribute, while installing (not when compiling). There, there's no problem with the colon, so you can simply use the \\?\ prefix.
Source: "\\?\Volume{bb919c3e-bdb1-42b8-9601-6715becd8683}\{#SourcePath}Encore.Warehouse.UI\bin\Warehouse_Release\warehouse\*"; \
DestDir: "\\?\{app}\warehouse"; Flags: ignoreversion recursesubdirs createallsubdirs
Of course, if the problem is caused by a root path being too long already, you can fix the problem simply be moving the source files to a folder with a shorter path. Or you can use subst to create a virtual drive or you can create a symlink/directory junction.
Falling into the same issue, here below is more details on workaround with subst as described in comments and accepted answer.
Here below is content of some precompile.bat file to associate some local path with the A: drive letter
#echo off
REM NB: Removing any previous association to be sure new one will work
subst A: /D 1> NUL 2>&1
subst A: "%~dp0.."
And content of some postcompile.bat to remove association in the end
#echo off
subst A: /D 1> NUL 2>&1
NB1: Careful, once associated, it is difficult to navigate upper paths directly within the .iss script because A:\.. is still A:\ ! (I fall into the issue, so worth to know). Association should then be made with closest top-most folder of all required files directly in precompile.bat.
NB2: I don't know if feasible to remind for any previous association and restore it in the end
These steps can be added to the .iss script as follow:
[PreCompile]
Name: "precompile.bat"; Flags: cmdprompt redirectoutput
[PostCompile]
Name: "postcompile.bat"; Flags: cmdprompt redirectoutput
Note finally that [PreCompile] and [PostCompile] sections will execute from the IDE only. They won't execute from the command line (at least with inno 5.5.9), I also fall into this... so complete compilation from command line should look like:
call "precompile.bat"
call "%ProgramFiles(x86)%\Inno Setup 5\ISCC.exe" "myscript.iss"
call "postcompile.bat"
NB: I think calling Compil32.exe /cc "myscript.iss" (IDE compiler) instead of ISCC.exe (command-line compiler) should run [PreCompile] and [PostCompile] sections but in my case I had to pass extra /D options to the compiler so it was not possible to call Compil32.exe directly.

Extract multiple files from installer before installation begins in Inno Setup

I have few SQL script files which have to run before installation begin. The reason is if SQL scripts run successfully only, I want to do the installation.
If the SQL scripts need to run after the installation, I can copy the files to {app} path and run the files from there. But the requirement is run the files before installation begins. I am confused. What is the best way of doing it?
Say for example if it is a single file I can put it under Files section and can use ExtractTemporaryFile('FileName');
But as I mentioned, I have many files (in SQLSCRIPTS folder). What is the better way? (One solution is I can make it as a single file by zipping it and then unzip it)
[Files]
Source: "C:\\SQLSCRIPTS\\*"; DestDir: "{app}"; Flags: dontcopy
To extract multiple files from installer, use the ExtractTemporaryFiles, like:
ExtractTemporaryFiles('*.sql');

How to create an installer using Inno Setup which extracts the contents of a .rar archive?

I want to create an installer in Inno Setup which extracts the content of pre created Data.rar archive. I mean it should treat the contents of the rar archive as files and folders of application.
A generic way to use an external extraction utility with Inno Setup:
create the archive
embed the archive to the installer
embed a tool that can extract the archive to the installer
make the installer extract the archive and the tool to a temporary location on target machine - {tmp}
make the installer run the tool to extract the archive
[Files]
Source: "UnRAR.exe"; DestDir: "{tmp}"; Flags: deleteafterinstall
Source: "Data.rar"; DestDir: "{tmp}"; Flags: deleteafterinstall nocompression
[Run]
Filename: "{tmp}\UnRAR.exe"; Parameters: "x ""{tmp}\Data.rar"" ""{app}"""
If you want to present a progress of the decompression, you will have to parse the UnRAR output. For an example (on Arc), see How to add .arc decompression to Inno Setup?
Or use UnRAR.dll, similarly as unarc.dll is used in Inno Setup - How to add cancel button to decompressing page?
Note that the UnRAR.exe tool is free and can be used for these purposes. An extract from its license.txt:
The UnRAR utility may be freely distributed. It is allowed
to distribute UnRAR inside of other software packages.

Inno Setup AfterInstall function called for each file

I want to call a function after installing a folder, but the InstallEnv function is seems to be called several times, maybe for each file is the folder (to be confirmed). Is there a way to call it only once after it installed all of those files? I can't use the Run section because I want to do error catching with the return code.
Source: "InputFiles\virtualenv-1.8.2\*"; DestDir: "{tmp}/virtualenv"; \
Flags: recursesubdirs; AfterInstall: InstallEnv;
There is no way to call it at the end of the installation of that group of files, from within a single entry. However it is possible to call the function at the appropriate time by making use of a dummy entry:
[Files]
Source: "InputFiles\virtualenv-1.8.2\*"; DestDir: "{tmp}\virtualenv"; Flags: recursesubdirs
Source: dummy.txt; DestDir: {tmp}; AfterInstall: InstallEnv
The Source file must exist but it can be a zero-byte file. As installation is into {tmp} it will be deleted after install anyway so its contents are irrelevant.
This works because [Files] entries are installed in the order specified.
Yes, it executes once per each file. The reference says about it (emphasized by me):
A BeforeInstall or AfterInstall function for a [Files] section entry
using a wildcard is called once per file matching the wildcard. Use
CurrentFileName to check for which file the function is called.
And no, there is no way to call it once after all the files are installed. If you were going to run it only once, it wouldn't be a problem, since you might declare a flag variable, indicating that the function was already called, but you want to detect if it's the last call, and for this there is no workaround.
Well, maybe if you would know, which file will be the latest installed from that folder, you might check that against the result of the CurrentFileName function call, but I doubt that you can determine which one will be installed as last at compilation time (since at runtime, there is currently no way to get list of files to be installed).

Resources