fvarrui / JavaPackager

:package: Gradle/Maven plugin to package Java applications as native Windows, MacOS, or Linux executables and create installers for them.
GNU General Public License v3.0
1.07k stars 134 forks source link

VTL files instead of ISS #308

Closed valimaties closed 1 year ago

valimaties commented 1 year ago

I'm submitting a…

Short description of the issue/suggestion: Why do we have to use a iss.vtl file to load a custom iss and not adding some path to the custom iss file in our pom configuration? I tried to rename my custom iss file to "iss.vtl" in "target\assets\windows" folder, but it doesn't work like this, seems the compiler uses the default iss script.

Steps to reproduce the issue/enhancement:

  1. [First Step]
  2. [Second Step]
  3. [Other Steps...]

What is the expected behavior? Simply using a path to the new/custom iss file instead using a vtl file. This could be set in pom file configuration tag of this plugin.

What is the current behavior? I cannot use my custom iss file, which add permissions to installation folder.

[Dirs] 
Name: "{app}"; Permissions: everyone-full

In Windows 11 even if the installer is opened with Administrator rights the folder created in Program Files does not have full access, so the application crushes when starts, because it must write some ini file when starts, if this ini file is not present on the installation folder.

Do you have outputs, screenshots, demos or samples which demonstrate the problem or enhancement?

What is the motivation / use case for changing the behavior? I don't know VTL format and I don't know how to convert this iss file to vtl.

Please tell us about your environment:

Other information (e.g. related issues, suggestions how to fix, links for us to have context)

valimaties commented 1 year ago

I used ChatGPT to convert iss file to vtl. Anyway, seems that the plugin does not uses the vtl file, instead it creates the default iss file and uses that one. Is truly annoying ...

fvarrui commented 1 year ago

Hi @valimaties! JP looks for assets in the ${assetsDir} directory, which is set to ${project.basedir}/assets by default. You should move your custom target/assets/windows/iss.vtl template to ${project.basedir}/assets/windows or change the assetsDir property to reference ${project.build.directory}/assets, but it's not a good idea to store customized files in target directory, as they are removed when cleaning or overwritten when building.

valimaties commented 1 year ago

Good to know. I didn't saw these explanation nowhere. Thanks, I will try it.

fvarrui commented 1 year ago

Good to know. I didn't saw these explanation nowhere. Thanks, I will try it.

Sorry, maybe these explanations are a bit poor and can be improved with some examples. Please, let me know if you finally manage to fix this issue and any feedback on how the docs can be improved is welcome. PRs are accepted πŸ˜…

Thanks!!!

valimaties commented 1 year ago

I've saw those explanations, but my only one assets folder is the one from target folder of my app... I don't have an assets folder in my app/resources ... Maybe if there was some explanations about the "${assetsDir}" it must be a folder in anyone app tree folders, with concrete examples, I have had known..
However, I wonder why did you chose these vtl files, instead of the simple iss ... I see that this plugin will create a default iss file in the target/assets folder, with the name of my app. Why didn't we use an iss file instead of vtl? We have InnoSetup installed, we can create a script there, modify it, we can do anything in InnoSetup, and use the JP plugin to build using the custom iss file. Don't you think is a better idea than using a vtl file?

So... If this is my project structure, where do I have to put the iss.vtl file?

src
  main
    java
      com.myappname.myapp
        <java-classes>
    resources
      com.myappname.myapp
        <resource-files>

This is the INFO line from package (everytime):

Executing command: cmd.exe /s /c "iscc "/OD:\Java\<MyAppName>\target" /F<MyAppName>_1.0 "D:\Java\<MyAppName>\target\assets\<MyAppName>.iss""
fvarrui commented 1 year ago

VTL are Velocity Templates, and it allows us to create dynamic files (where certain file contents are subject to certain conditions or require extra processing) from a template. We can say that a VTL file is a text file with variables (we can even embed code in its scripting language). So, do you propose to use static files instead of VTL templates and manually create all your intermediate files (ISS, INI, Plist, exe.manifest, ...)? All those files depends on the plugin configuration, so they should be dynamic.

When I started developing JP I had in mind that I wanted the developer to not have to worry about the details of generating installation artifacts, native binaries and some other artifacts ... so, JP tries to generate all needed artifacts and intermmediate files (as ISS files) for you. But ... I also wanted to give the possibility to the developers to use their own files during some building processes, so I included assetsDir property.

Do you want to use your own ISS file? Ok, this is the way:

  1. Create next folders in the root dir of your project:
    ${basedir}/
    assets/
      windows/
  2. Create your own static ISS file, rename it as iss.vtl and copy it into the previous folder:
    ${basedir}/
    assets/
      windows/
          iss.vtl
  3. Build your project:
    mvn package

    During the building process, JP will render your iss.vtl template in target/assets/<your project name>.iss and execute iscc (Inno Setup Command-line Compiler) to generate your Windows installer. If you don't include Velocity scriptlets inside your iss.vtl, your iss.vtl is literally copied to target/assets as <your project name>.iss.

fvarrui commented 1 year ago

image

image

image

fvarrui commented 1 year ago

So ... do you need next lines in your ISS file?

[Dirs] 
Name: "{app}"; Permissions: everyone-full

this should be your template ${basedir}/assets/windows/iss.vtl:

\#define MyAppName "${info.name}"
\#define MyAppVersion "${info.version}"
\#define MyAppPublisher "${info.organizationName}"
\#define MyAppURL "$!{info.organizationUrl}"
\#define MyAppExeName "${info.executable.name}"
\#define MyAppFolder "${info.name}"
\#define MyAppLicense "$!{info.licenseFile.absolutePath}"
\#define MyAppIcon "${info.iconFile.absolutePath}"

[Setup]
AppId={{{#MyAppName}}}
AppName={#MyAppName}
AppVersion={#MyAppVersion}
AppVerName={#MyAppName} {#MyAppVersion}
AppPublisher={#MyAppPublisher}
AppPublisherURL={#MyAppURL}
AppSupportURL={#MyAppURL}
AppUpdatesURL={#MyAppURL}
DefaultDirName={autopf}\{#MyAppFolder}
#if ($info.winConfig.disableDirPage)
DisableDirPage=yes
#else
DisableDirPage=no
#end
#if ($info.winConfig.disableProgramGroupPage)
DisableProgramGroupPage=yes
#else
DefaultGroupName={#MyAppName}
DisableProgramGroupPage=no
#end
#if ($info.winConfig.disableFinishedPage)
DisableFinishedPage=yes
#else
DisableFinishedPage=no
#end
#if ($info.winConfig.disableWelcomePage)
DisableWelcomePage=yes
#else
DisableWelcomePage=no
#end
#if ($info.winConfig.setupMode.name() == "installForAllUsers") 
PrivilegesRequired=admin
PrivilegesRequiredOverridesAllowed=commandline
#elseif ($info.winConfig.setupMode.name() == "installForCurrentUser") 
PrivilegesRequired=lowest
PrivilegesRequiredOverridesAllowed=commandline
#else 
PrivilegesRequiredOverridesAllowed=commandline dialog
#end
LicenseFile={#MyAppLicense}
SetupIconFile={#MyAppIcon}
UninstallDisplayIcon={app}\{#MyAppExeName}
Compression=lzma
SolidCompression=yes
ArchitecturesInstallIn64BitMode=x64

[Languages]
#foreach ($language in $info.winConfig.setupLanguages.entrySet())
Name: "${language.key}"; MessagesFile: "${language.value}"
#end

[Tasks]
#if ($info.winConfig.createDesktopIconTask)
Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: unchecked
#end

[Registry]
#if ($info.winConfig.registry)
#foreach ($entry in $info.winConfig.registry.entries)
Root: ${entry.root}; Subkey: "${entry.subkey}"; ValueType: ${entry.valueTypeAsInnoSetupString}; ValueName: "${entry.valueName}"; ValueData: "${entry.valueData}"; Flags: uninsdeletevalue
#end
#end
#foreach ($fileAssociation in $info.fileAssociations)
; ${fileAssociation.extension} extension file association
Root: HKA; Subkey: "Software\Classes\.${fileAssociation.extension}\OpenWithProgids"; ValueType: string; ValueName: "${info.name}.${fileAssociation.extension}"; ValueData: ""; Flags: uninsdeletevalue
Root: HKA; Subkey: "Software\Classes\\${info.name}.${fileAssociation.extension}"; ValueType: string; ValueName: ""; ValueData: "${fileAssociation.description}"; Flags: uninsdeletekey
Root: HKA; Subkey: "Software\Classes\\${info.name}.${fileAssociation.extension}\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\\${info.executable.name},0"; Flags: uninsdeletevalue
Root: HKA; Subkey: "Software\Classes\\${info.name}.${fileAssociation.extension}\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\\${info.executable.name}"" ""%1"""; Flags: uninsdeletevalue
Root: HKA; Subkey: "Software\Classes\Applications\\${info.executable.name}\SupportedTypes"; ValueType: string; ValueName: ".${fileAssociation.extension}"; ValueData: ""; Flags: uninsdeletevalue
#end

[Files]
Source: "${info.appFolder}\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs

[Icons]
Name: "{autoprograms}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"; IconFilename: "{app}\\${info.iconFile.name}"
#if ($info.winConfig.createDesktopIconTask)
Name: "{autodesktop}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"; IconFilename: "{app}\\${info.iconFile.name}"; Tasks: desktopicon
#end

[Run]
#if (!$info.winConfig.disableRunAfterInstall)
Filename: "{app}\{#MyAppExeName}"; Description: "{cm:LaunchProgram,{#StringChange(MyAppName, '&', '&&')}}"; Flags: nowait postinstall skipifsilent runascurrentuser
#end

[Code]

function GetInstallLocation(): String;
var
    unInstPath: String;
    installLocation: String;
begin
    unInstPath := ExpandConstant('Software\Microsoft\Windows\CurrentVersion\Uninstall\{#emit SetupSetting("AppId")}_is1');
    installLocation := '';
    if not RegQueryStringValue(HKLM, unInstPath, 'InstallLocation', installLocation) then
        RegQueryStringValue(HKCU, unInstPath, 'InstallLocation', installLocation);
    Result := RemoveQuotes(installLocation);
end;

#if ($info.winConfig.removeOldLibs)
procedure RemoveOldLibs();
var
    installLocation: String;
    libsLocation: String;
begin
    installLocation := GetInstallLocation();
    if installLocation <> '' then 
    begin
        libsLocation := installLocation + '${info.libsFolder.name}';
        DelTree(libsLocation, True, True, True);
    end;
end;
#end

procedure CurStepChanged(CurStep: TSetupStep);
begin
    if CurStep = ssInstall then
    begin
#if ($info.winConfig.removeOldLibs)
        RemoveOldLibs();
#end
    end;
end;

[Dirs] 
Name: "{app}"; Permissions: everyone-full

Note that the last 2 lines are yours, as we are adding the new section [Dirs] in the file.

valimaties commented 1 year ago

Ok, that helps me. Now the package goal executes my vtl file. I have to try to convert it better, seems ChatGPT did not convert well and some error is encountered. I don't know how to convert the script I made in Inno Setup to vtl file, so for now, I will use directly Inno Setup to generate installer. Is more simple for me like that than trying to convert the iss to vtl, is time consuming right now, and I want to use my time to do my programming job, not trying to find a way to convert that script. That's why I wanted somehow to use the iss file in that assetsDir/windows folder instead of an vtl file.

If you think of a way to use the custom iss (if it is there) instead of an vtl file, I will use the package goal with the < generateSetup > parameter in pom file on true. Till then, it will stay on false.

Thanks again and sorry for your lost time with me 😁

fvarrui commented 1 year ago

You can find more info about Velocity templates here. Maybe this sheds a little more light on the issue.

Reading again your description of the issue I found this:

In Windows 11 even if the installer is opened with Administrator rights the folder created in Program Files does not have full access, so the application crushes when starts, because it must write some ini file when starts, if this ini file is not present on the installation folder.

This INI files which are distributed with your app, which I guess they are next to the EXE file in "Program Files" ... are the same for all your users? I mean ... Do all users have to share the same configuration?

valimaties commented 1 year ago

The ini file is not distributed with the exe. Each PC will have its config.ini file. This file will store some data for each PC. So, the application must have full rights on read-write. In fact there are some other files too which are created in runtime. If the folder does not come with full permisions, the app will crash because is unable to create those files.

fvarrui commented 1 year ago

Ok, that helps me. Now the package goal executes my vtl file. I have to try to convert it better, seems ChatGPT did not convert well and some error is encountered. I don't know how to convert the script I made in Inno Setup to vtl file, so for now, I will use directly Inno Setup to generate installer. Is more simple for me like that than trying to convert the iss to vtl, is time consuming right now, and I want to use my time to do my programming job, not trying to find a way to convert that script. That's why I wanted somehow to use the iss file in that assetsDir/windows folder instead of an vtl file.

If you think of a way to use the custom iss (if it is there) instead of an vtl file, I will use the package goal with the < generateSetup > parameter in pom file on true. Till then, it will stay on false.

Ok, but as I explained before, you just have to rename your own ".iss" file into "iss.vtl" if you don't want to deal with VTL. That's all. This is the way to use the custom iss, just renaming a file.

Thanks again and sorry for your lost time with me 😁

Don't worry! Happy to help πŸ˜ƒ

fvarrui commented 1 year ago

The ini file is not distributed with the exe. Each PC will have its config.ini file. This file will store some data for each PC. So, the application must have full rights on read-write. In fact there are some other files too which are created in runtime. If the folder does not come with full permisions, the app will crash because is unable to create those files.

I'm not sure exactly what is your case, but I usually store config files in the user's profile: C:\Users\<me>\.myapp\config.ini

valimaties commented 1 year ago

I've see your vtl code right now... is in tests right now... thanks πŸ˜πŸ€—

fvarrui commented 1 year ago

The ini file is not distributed with the exe. Each PC will have its config.ini file. This file will store some data for each PC. So, the application must have full rights on read-write. In fact there are some other files too which are created in runtime. If the folder does not come with full permisions, the app will crash because is unable to create those files.

I'm not sure exactly what is your case, but I usually store config files in the user's profile: C:\Users\<me>\.myapp\config.ini

Oh! and AFAIK, maybe you can store shared configuration in \ProgramData\myapp\config.ini ... I think that all users can access those files. I think that giving full access to your binaries is not a good idea.

fvarrui commented 1 year ago

I've see your vtl code right now... is in tests right now... thanks πŸ˜πŸ€—

Great! Give me some feedback, please

valimaties commented 1 year ago

Ok, that script works. When I have time I will try to see what you've done there 😁

Thank you for all your help! ❀️