diff --git a/v3/cmd/wails3/main.go b/v3/cmd/wails3/main.go index 34920ae11fc..11648ac839a 100644 --- a/v3/cmd/wails3/main.go +++ b/v3/cmd/wails3/main.go @@ -46,6 +46,8 @@ func main() { generate.NewSubCommandFunction("constants", "Generate JS constants from Go", commands.GenerateConstants) generate.NewSubCommandFunction(".desktop", "Generate .desktop file", commands.GenerateDotDesktop) generate.NewSubCommandFunction("appimage", "Generate Linux AppImage", commands.GenerateAppImage) + generate.NewSubCommandFunction("rpm", "Generate Linux RPM", commands.GenerateRPM) + generate.NewSubCommandFunction("runtime", "Generate the latest compiled runtime", commands.GenerateRuntime) plugin := app.NewSubCommand("plugin", "Plugin tools") //plugin.NewSubCommandFunction("list", "List plugins", commands.PluginList) diff --git a/v3/internal/commands/build_assets/rpm/app.spec b/v3/internal/commands/build_assets/rpm/app.spec new file mode 100644 index 00000000000..489cb9543a0 --- /dev/null +++ b/v3/internal/commands/build_assets/rpm/app.spec @@ -0,0 +1,40 @@ +%global debug_package %{nil} + +Name: @NAME@ +Summary: @SUMMARY@ +Version: @VERSION@ +Release: @RPM_RELEASE@%{?dist} +License: @LICENSE@ + +Source0: %{name} +Source1: %{name}.desktop +Source2: %{name}.png + +%description +@DESCRIPTION@ + +%install +mkdir -p %{buildroot}%{_bindir} %{buildroot}%{_datadir}/{applications,pixmaps} + +cp %{SOURCE0} %{buildroot}%{_bindir}/%{name} +cp %{SOURCE1} %{buildroot}%{_datadir}/applications/%{name}.desktop +cp %{SOURCE2} %{buildroot}%{_datadir}/pixmaps/%{name}.png + +%post +# Install the desktop entry +update-desktop-database &> /dev/null || : + +%postun +# Uninstall the desktop entry +update-desktop-database &> /dev/null || : + + +%files +%{_bindir}/%{name} +%{_datadir}/applications/%{name}.desktop +%{_datadir}/pixmaps/%{name}.png + + +%changelog +* @RELEASE_DATE@ @AUTHOR@ <@AUTHOR_EMAIL@> - %{version}-%{release} +- Initial build diff --git a/v3/internal/commands/rpm.go b/v3/internal/commands/rpm.go new file mode 100644 index 00000000000..74bf7349ae5 --- /dev/null +++ b/v3/internal/commands/rpm.go @@ -0,0 +1,84 @@ +package commands + +import ( + _ "embed" + "errors" + "fmt" + "github.com/pterm/pterm" + "github.com/wailsapp/wails/v3/internal/s" + "os" + "path/filepath" +) + +type GenerateRPMOptions struct { + Binary string `description:"The binary to package including path"` + Icon string `description:"Path to the icon"` + DesktopFile string `description:"Path to the desktop file"` + OutputDir string `description:"Path to the output directory" default:"."` + BuildDir string `description:"Path to the build directory"` +} + +func GenerateRPM(options *GenerateRPMOptions) error { + + defer func() { + pterm.DefaultSpinner.Stop() + }() + + if options.Binary == "" { + return fmt.Errorf("binary not provided") + } + if options.Icon == "" { + return fmt.Errorf("icon path not provided") + } + if options.DesktopFile == "" { + return fmt.Errorf("desktop file path not provided") + } + if options.BuildDir == "" { + // Create temp directory + var err error + options.BuildDir, err = os.MkdirTemp("", "wails-rpm-*") + if err != nil { + return err + } + } + var err error + options.OutputDir, err = filepath.Abs(options.OutputDir) + if err != nil { + return err + } + + pterm.Println(pterm.LightYellow("RPM Generator v1.0.0")) + + return generateRPM(options) +} + +func generateRPM(options *GenerateRPMOptions) error { + numberOfSteps := 5 + p, _ := pterm.DefaultProgressbar.WithTotal(numberOfSteps).WithTitle("Generating RPM").Start() + + // Get the last path of the binary and normalise the name + name := normaliseName(filepath.Base(options.Binary)) + + log(p, "Preparing RPMBUILD Directory: "+options.BuildDir) + + sources := filepath.Join(options.BuildDir, "SOURCES") + s.MKDIR(sources) + s.COPY(options.Binary, sources) + s.COPY(options.DesktopFile, sources) + s.COPY(filepath.Join(options.BuildDir, name+".png"), sources) + s.CHMOD(filepath.Join(sources, filepath.Base(options.Binary)), 0755) + + // Build RPM + if !s.EXISTS("/usr/bin/rpmbuild") { + return errors.New("You need to install \"rpm-build\" tool to build RPM.") + } + log(p, fmt.Sprintf("Running rpmbuild -bb --define \"_rpmdir %s\" --define \"_sourcedir %s\" %s.spec", options.BuildDir, sources, name)) + _, err := s.EXEC(fmt.Sprintf("rpmbuild -bb --define \"_rpmdir %s\" --define \"_sourcedir %s\" %s.spec", options.BuildDir, sources, name)) + if err != nil { + fmt.Println(err.Error()) + return err + } + + log(p, "RPM created.") + return nil +} diff --git a/v3/internal/doctor/packagemanager/dnf.go b/v3/internal/doctor/packagemanager/dnf.go index f9ad17b1ea9..894579a0858 100644 --- a/v3/internal/doctor/packagemanager/dnf.go +++ b/v3/internal/doctor/packagemanager/dnf.go @@ -43,6 +43,9 @@ func (y *Dnf) Packages() Packagemap { {Name: "npm", SystemPackage: true}, {Name: "nodejs-npm", SystemPackage: true}, }, + "rpm-build": []*Package{ + {Name: "rpm-build", SystemPackage: true, Optional: true}, + }, } } diff --git a/v3/internal/templates/_common/Taskfile.tmpl.yml b/v3/internal/templates/_common/Taskfile.tmpl.yml index 7be6fd47aa1..4caf82b26f7 100644 --- a/v3/internal/templates/_common/Taskfile.tmpl.yml +++ b/v3/internal/templates/_common/Taskfile.tmpl.yml @@ -287,6 +287,7 @@ tasks: vars: PRODUCTION: "true" cmds: + - task: create:rpm - task: create:appimage create:appimage: @@ -301,7 +302,7 @@ tasks: cmds: # Copy binary + icon to appimage dir - cp {{ "{{.APP_BINARY}}" }} {{ "{{.APP_NAME}}" }} - - cp ../appicon.png appicon.png + - cp ../appicon.png '{{ "{{.APP_NAME}}" }}.png' # Generate AppImage - wails3 generate appimage -binary {{ "{{.APP_NAME}}" }} -icon {{ "{{.ICON}}" }} -desktopfile {{ "{{.DESKTOP_FILE}}" }} -outputdir {{ "{{.OUTPUT_DIR}}" }} -builddir {{ "{{.ROOT_DIR}}" }}/build/appimage vars: @@ -311,6 +312,48 @@ tasks: DESKTOP_FILE: '{{ "{{.APP_NAME}}" }}.desktop' OUTPUT_DIR: '../../bin' + create:rpm: + summary: Creates a RPM package + dir: build/rpm + platforms: [ linux ] + deps: + - task: build:linux + vars: + PRODUCTION: "true" + - task: generate:linux:dotdesktop + cmds: + # Copy binary + icon to rpmbuild dir + - cp {{ "{{.APP_BINARY}}" }} {{ "{{.APP_NAME}}" }} + - cp ../appicon.png '{{ "{{.APP_NAME}}" }}.png' + - cp ../rpm/app.spec '{{ "{{.APP_NAME}}" }}.spec' + - sed -i -e 's/@NAME@/{{ "{{.APP_NAME}}" }}/g' + -e 's/@SUMMARY@/{{ "{{.APP_SUMMARY}}" }}/g' + -e 's/@VERSION@/{{ "{{.APP_VERSION}}" }}/g' + -e 's/@RPM_RELEASE@/{{ "{{.RPM_RELEASE}}" }}/g' + -e 's/@DESCRIPTION@/{{ "{{.APP_DESC}}" }}/g' + -e 's/@AUTHOR@/{{ "{{.APP_AUTHOR}}" }}/g' + -e 's/@AUTHOR_EMAIL@/{{ "{{.AUTHOR_EMAIL}}" }}/g' + -e 's/@LICENSE@/{{ "{{.APP_LICENSE}}" }}/g' + -e 's/@RELEASE_DATE@/{{ "{{.RELEASE_DATE}}" }}/g' + {{ "{{.APP_NAME}}" }}.spec + # Generate RPM + - wails3 generate rpm -binary {{ "{{.APP_NAME}}" }} -icon {{ "{{.ICON}}" }} -desktopfile {{ "{{.DESKTOP_FILE}}" }} -outputdir {{ "{{.OUTPUT_DIR}}" }} -builddir {{ "{{.ROOT_DIR}}" }}/build/rpm + - mv {{ "{{.ROOT_DIR}}" }}/build/rpm/$(uname -m)/*.rpm {{ "{{.OUTPUT_DIR}}" }} + vars: + APP_SUMMARY: 'Hello Wails' + APP_VERSION: '1.0.0' + RPM_RELEASE: '1' + APP_DESC: 'Hello Wails is demo application developed on Wails.' + APP_URL: 'https://github.com/wailsapp/wails' + APP_AUTHOR: "Lea Anthony" + AUTHOR_EMAIL: "lea.anthony@gmail.com" + APP_LICENSE: "MIT" + APP_BINARY: '../../bin/{{ "{{.APP_NAME}}" }}' + RELEASE_DATE: {{ "'{{now | date \"Mon Jan 02 2006\"}}'" }} + ICON: '../appicon.png' + DESKTOP_FILE: '{{ "{{.APP_NAME}}" }}.desktop' + OUTPUT_DIR: '../../bin' + generate:linux:dotdesktop: summary: Generates a `.desktop` file dir: build @@ -318,10 +361,12 @@ tasks: - "appicon.png" generates: - '{{ "{{.ROOT_DIR}}"}}/build/appimage/{{ "{{.APP_NAME}}" }}.desktop' + - '{{ "{{.ROOT_DIR}}"}}/build/rpm/{{ "{{.APP_NAME}}" }}.desktop' cmds: - - mkdir -p {{ "{{.ROOT_DIR}}"}}/build/appimage + - mkdir -p {{ "{{.ROOT_DIR}}"}}/build/{appimage,rpm} # Run `wails3 generate .desktop -help` for all the options - wails3 generate .desktop -name "{{ "{{.APP_NAME}}" }}" -exec "{{ "{{.EXEC}}" }}" -icon "{{ "{{.ICON}}" }}" -outputfile {{ "{{.ROOT_DIR}}"}}/build/appimage/{{ "{{.APP_NAME}}" }}.desktop -categories "{{ "{{.CATEGORIES}}" }}" + - wails3 generate .desktop -name "{{ "{{.APP_NAME}}" }}" -exec "{{ "{{.EXEC}}" }}" -icon "{{ "{{.ICON}}" }}" -outputfile {{ "{{.ROOT_DIR}}"}}/build/rpm/{{ "{{.APP_NAME}}" }}.desktop -categories "{{ "{{.CATEGORIES}}" }}" # -comment "A comment" # -terminal "true" # -version "1.0" @@ -333,7 +378,7 @@ tasks: vars: APP_NAME: '{{ "{{.APP_NAME}}" }}' EXEC: '{{ "{{.APP_NAME}}" }}' - ICON: 'appicon' + ICON: '{{ "{{.APP_NAME}}" }}' CATEGORIES: 'Development;' OUTPUTFILE: '{{ "{{.ROOT_DIR}}"}}/build/appimage/{{ "{{.APP_NAME}}" }}.desktop' diff --git a/website/src/pages/changelog.mdx b/website/src/pages/changelog.mdx index 910b6d3af95..b761de80562 100644 --- a/website/src/pages/changelog.mdx +++ b/website/src/pages/changelog.mdx @@ -20,6 +20,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Add Apple Silicon hardware detection to `wails doctor`. Changed by @almas1992 in [PR](https://github.com/wailsapp/wails/pull/3129) - Remove quarantine attribute on macOS binaries. Changed by @leaanthony in [PR](https://github.com/wailsapp/wails/pull/3118) - Added documentation for a common GStreamer error on Linux systems. Changed by [@mkwsnyder](https://github.com/mkwsnyder) in [PR](https://github.com/wailsapp/wails/pull/3134) +- Added RPM packaging support. Added by @istiak101 in [PR](https://github.com/wailsapp/wails/pull/3265) ### Fixed - Dev mode now adapts to what OS it is ran on to source the correct executable. Fixed by [@atterpac](https://github.com/atterpac) in [PR](https://github.com/wailsapp/wails/pull/3412)