Skip to content

Commit

Permalink
[FEAT] Add support for Pacman (Arch Linux) (#198)
Browse files Browse the repository at this point in the history
* feat: add pacman package support

* fix: null issue

* fix: null issue

* fix: null issue

* fix: formatting of INSTALL file

* fix: also show error

* fix: try to fix error

* fix: try to fix error

* fix: try to fix error

* fix: try to fix error

* fix: try to fix error

* fix: try to fix error

* fix: try to fix error

* fix: rewrite command

* fix: rewrite command

* fix: error in mv

* feat: add sample make_config.yaml for pacman

* chore: update top documentation

* chore: update documentation for pacman

* fix: arch for arm

* fix: add post upgrade scripts

* fix: package usr directory too

* fix: don't block rpm build if metainfo not found
  • Loading branch information
prateekmedia authored Jun 23, 2024
1 parent b5b3460 commit d893621
Show file tree
Hide file tree
Showing 10 changed files with 656 additions and 9 deletions.
6 changes: 6 additions & 0 deletions examples/hello_world/distribute_options.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,12 @@ releases:
target: deb
build_args:
profile: true
- name: linux-pacman
package:
platform: linux
target: pacman
build_args:
profile: true
- name: linux-zip
package:
platform: linux
Expand Down
100 changes: 100 additions & 0 deletions examples/hello_world/linux/packaging/pacman/make_config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
# the name used to display in the OS. Specifically desktop
# entry name
display_name: Hello World

# package name for debian/apt repository
# the name should be all lowercase with -+.
package_name: hello-world

maintainer:
name: LiJianying
email: [email protected]

# the size of binary in kilobyte
installed_size: 6604

# direct dependencies required by the application
# refer: https://www.debian.org/doc/debian-policy/ch-relationships.html
# dependencies:
# - libkeybinder-3.0-0 (>= 0.3.2)

# refer: https://www.debian.org/doc/debian-policy/ch-relationships.html
# build_dependencies_indep:
# - texinfo

# refer: https://www.debian.org/doc/debian-policy/ch-relationships.html
# build_dependencies:
# - kernel-headers-2.2.10 [!hurd-i386]
# - gnumach-dev [hurd-i386]
# - libluajit5.1-dev [i386 amd64 kfreebsd-i386 armel armhf powerpc mips]

# refer: https://www.debian.org/doc/debian-policy/ch-relationships.html
# recommended_dependencies:
# - neofetch

# refer: https://www.debian.org/doc/debian-policy/ch-relationships.html
# suggested_dependencies:
# - libkeybinder-3.0-0 (>= 0.3.2)

# refer: https://www.debian.org/doc/debian-policy/ch-relationships.html
# enhances:
# - spotube

# refer: https://www.debian.org/doc/debian-policy/ch-relationships.html
# pre_dependencies:
# - libc6

# refer: https://www.debian.org/doc/debian-policy/ch-relationships.html#packages-which-break-other-packages-breaks
# breaks:
# - libspotify (<< 3.0.0)

# refer: https://www.debian.org/doc/debian-policy/ch-relationships.html#conflicting-binary-packages-conflicts
# conflicts:
# - spotify

# refer: https://www.debian.org/doc/debian-policy/ch-relationships.html#virtual-packages-provides
# provides:
# - libx11

# refer: https://www.debian.org/doc/debian-policy/ch-relationships.html#overwriting-files-and-replacing-packages-replaces
# replaces:
# - spotify

postinstall_scripts:
- echo `Installed my awesome app`
postuninstall_scripts:
- echo `Surprised Pickachu face`

# application icon path relative to project url
icon: assets/logo.png

keywords:
- Hello
- World
- Test
- Application

# a name to categorize the app into a section of application
generic_name: Music Application

# supported mime types that can be opened using this application
# supported_mime_type:
# - audio/mpeg

metainfo: linux/packaging/helloworld.appdata.xml

# shown when right clicked the desktop entry icons
# actions:
# - Gallery
# - Create

# the categories the application belong to
# refer: https://specifications.freedesktop.org/menu-spec/latest/
categories:
- Music
- Media

# let OS know if the application can be run on start_up. If it's false
# the application will deny to the OS if it was added as a start_up
# application
startup_notify: true
3 changes: 2 additions & 1 deletion packages/flutter_app_builder/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ dependencies:
pub_semver: ^2.1.0
pubspec_parse: ^1.1.0
recase: ^4.1.0
shell_executor: ^0.1.5
shell_executor:
path: ../shell_executor

dev_dependencies:
dependency_validator: ^3.0.0
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import 'dart:io';

import 'package:flutter_app_packager/src/api/app_package_maker.dart';
import 'package:flutter_app_packager/src/makers/makers.dart';
import 'package:flutter_app_packager/src/makers/pacman/app_package_maker_pacman.dart';

class FlutterAppPackager {
final List<AppPackageMaker> _makers = [
Expand All @@ -18,6 +19,7 @@ class FlutterAppPackager {
AppPackageMakerMsix(),
AppPackageMakerPkg(),
AppPackageMakerRPM(),
AppPackageMakerPacman(),
AppPackageMakerZip('linux'),
AppPackageMakerZip('macos'),
AppPackageMakerZip('windows'),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,197 @@
import 'dart:io';

import 'package:flutter_app_packager/src/api/app_package_maker.dart';
import 'package:flutter_app_packager/src/makers/pacman/make_pacman_config.dart';
import 'package:path/path.dart' as path;
import 'package:shell_executor/shell_executor.dart';

class AppPackageMakerPacman extends AppPackageMaker {
@override
String get name => 'pacman';
@override
String get platform => 'linux';
@override
bool get isSupportedOnCurrentPlatform => Platform.isLinux;
@override
String get packageFormat => 'pacman';

@override
MakeConfigLoader get configLoader {
return MakePacmanConfigLoader()
..platform = platform
..packageFormat = packageFormat;
}

@override
Future<MakeResult> make(MakeConfig config) {
return _make(
config.buildOutputDirectory,
outputDirectory: config.outputDirectory,
makeConfig: config as MakePacmanConfig,
);
}

Future<MakeResult> _make(
Directory appDirectory, {
required Directory outputDirectory,
required MakePacmanConfig makeConfig,
}) async {
final files = makeConfig.toFilesString();

Directory packagingDirectory = makeConfig.packagingDirectory;

/// Need to create following directories
/// /usr/share/$appBinaryName
/// /usr/share/applications
/// /usr/share/icons/hicolor/128x128/apps
/// /usr/share/icons/hicolor/256x256/apps
final applicationsDir =
path.join(packagingDirectory.path, 'usr/share/applications');
final icon128Dir = path.join(
packagingDirectory.path,
'usr/share/icons/hicolor/128x128/apps',
);
final icon256Dir = path.join(
packagingDirectory.path,
'usr/share/icons/hicolor/256x256/apps',
);
final metainfoDir =
path.join(packagingDirectory.path, 'usr/share/metainfo');
final mkdirProcessResult = await $('mkdir', [
'-p',
path.join(packagingDirectory.path, 'usr/share', makeConfig.appBinaryName),
applicationsDir,
if (makeConfig.metainfo != null) metainfoDir,
if (makeConfig.icon != null) ...[icon128Dir, icon256Dir],
]);

if (mkdirProcessResult.exitCode != 0) throw MakeError();

if (makeConfig.icon != null) {
final iconFile = File(makeConfig.icon!);
if (!iconFile.existsSync()) {
throw MakeError("provided icon ${makeConfig.icon} path wasn't found");
}

await iconFile.copy(
path.join(
icon128Dir,
makeConfig.appBinaryName + path.extension(makeConfig.icon!),
),
);
await iconFile.copy(
path.join(
icon256Dir,
makeConfig.appBinaryName + path.extension(makeConfig.icon!),
),
);
}
if (makeConfig.metainfo != null) {
final metainfoPath =
path.join(Directory.current.path, makeConfig.metainfo!);
final metainfoFile = File(metainfoPath);
if (!metainfoFile.existsSync()) {
throw MakeError("Metainfo $metainfoPath path wasn't found");
}
await metainfoFile.copy(
path.join(
metainfoDir,
makeConfig.appBinaryName + path.extension(makeConfig.metainfo!, 2),
),
);
}

// create & write the files got from makeConfig
final installFile = File(path.join(packagingDirectory.path, '.INSTALL'));
final pkgInfoFile = File(path.join(packagingDirectory.path, '.PKGINFO'));
final desktopEntryFile =
File(path.join(applicationsDir, '${makeConfig.appBinaryName}.desktop'));

if (!installFile.existsSync()) installFile.createSync();
if (!pkgInfoFile.existsSync()) pkgInfoFile.createSync();
if (!desktopEntryFile.existsSync()) desktopEntryFile.createSync();

await installFile.writeAsString(files['INSTALL']!);
await pkgInfoFile.writeAsString(files['PKGINFO']!);
await desktopEntryFile.writeAsString(files['DESKTOP']!);

// copy the application binary to /usr/share/$appBinaryName
await $('cp', [
'-fr',
'${appDirectory.path}/.',
'${packagingDirectory.path}/usr/share/${makeConfig.appBinaryName}/',
]);

// MTREE Metadata using bsdtar and fakeroot
ProcessResult mtreeResult = await $(
'bsdtar',
[
'-czf',
'.MTREE',
'--format=mtree',
'--options=!all,use-set,type,uid,gid,mode,time,size,md5,sha256,link',
'.PKGINFO',
'.INSTALL',
'usr',
],
environment: {
'LANG': 'C',
},
workingDirectory: packagingDirectory.path,
);
if (mtreeResult.exitCode != 0) {
throw MakeError(mtreeResult.stderr);
}

// create the pacman package using fakeroot and bsdtar
// fakeroot -- env LANG=C bsdtar -cf - .MTREE .PKGINFO * | xz -c -z - > $pkgname-$pkgver-$pkgrel-$arch.tar.xz

ProcessResult archiveResult = await $(
'bsdtar',
[
'-cf',
'temptar',
'.MTREE',
'.INSTALL',
'.PKGINFO',
'usr',
],
environment: {
'LANG': 'C',
},
workingDirectory: packagingDirectory.path,
);
if (archiveResult.exitCode != 0) {
throw MakeError(archiveResult.stderr);
}

ProcessResult processResult = await $(
'xz',
[
'-z',
'temptar',
],
workingDirectory: packagingDirectory.path,
);

if (processResult.exitCode != 0) {
throw MakeError(processResult.stderr);
}

// copy file from temptar.xz to the makeConfig.outputFile.path
final copyResult = await $(
'mv',
[
'${packagingDirectory.path}/temptar.xz',
makeConfig.outputFile.path,
],
);
if (copyResult.exitCode != 0) {
throw MakeError(copyResult.stderr);
}

packagingDirectory.deleteSync(recursive: true);
return MakeResult(makeConfig);
}
}
Loading

0 comments on commit d893621

Please sign in to comment.