Skip to content

Commit d893621

Browse files
authored
[FEAT] Add support for Pacman (Arch Linux) (#198)
* 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
1 parent b5b3460 commit d893621

10 files changed

Lines changed: 656 additions & 9 deletions

File tree

examples/hello_world/distribute_options.yaml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,12 @@ releases:
3838
target: deb
3939
build_args:
4040
profile: true
41+
- name: linux-pacman
42+
package:
43+
platform: linux
44+
target: pacman
45+
build_args:
46+
profile: true
4147
- name: linux-zip
4248
package:
4349
platform: linux
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
# the name used to display in the OS. Specifically desktop
2+
# entry name
3+
display_name: Hello World
4+
5+
# package name for debian/apt repository
6+
# the name should be all lowercase with -+.
7+
package_name: hello-world
8+
9+
maintainer:
10+
name: LiJianying
11+
email: lijy91@foxmail.com
12+
13+
# the size of binary in kilobyte
14+
installed_size: 6604
15+
16+
# direct dependencies required by the application
17+
# refer: https://www.debian.org/doc/debian-policy/ch-relationships.html
18+
# dependencies:
19+
# - libkeybinder-3.0-0 (>= 0.3.2)
20+
21+
# refer: https://www.debian.org/doc/debian-policy/ch-relationships.html
22+
# build_dependencies_indep:
23+
# - texinfo
24+
25+
# refer: https://www.debian.org/doc/debian-policy/ch-relationships.html
26+
# build_dependencies:
27+
# - kernel-headers-2.2.10 [!hurd-i386]
28+
# - gnumach-dev [hurd-i386]
29+
# - libluajit5.1-dev [i386 amd64 kfreebsd-i386 armel armhf powerpc mips]
30+
31+
# refer: https://www.debian.org/doc/debian-policy/ch-relationships.html
32+
# recommended_dependencies:
33+
# - neofetch
34+
35+
# refer: https://www.debian.org/doc/debian-policy/ch-relationships.html
36+
# suggested_dependencies:
37+
# - libkeybinder-3.0-0 (>= 0.3.2)
38+
39+
# refer: https://www.debian.org/doc/debian-policy/ch-relationships.html
40+
# enhances:
41+
# - spotube
42+
43+
# refer: https://www.debian.org/doc/debian-policy/ch-relationships.html
44+
# pre_dependencies:
45+
# - libc6
46+
47+
# refer: https://www.debian.org/doc/debian-policy/ch-relationships.html#packages-which-break-other-packages-breaks
48+
# breaks:
49+
# - libspotify (<< 3.0.0)
50+
51+
# refer: https://www.debian.org/doc/debian-policy/ch-relationships.html#conflicting-binary-packages-conflicts
52+
# conflicts:
53+
# - spotify
54+
55+
# refer: https://www.debian.org/doc/debian-policy/ch-relationships.html#virtual-packages-provides
56+
# provides:
57+
# - libx11
58+
59+
# refer: https://www.debian.org/doc/debian-policy/ch-relationships.html#overwriting-files-and-replacing-packages-replaces
60+
# replaces:
61+
# - spotify
62+
63+
postinstall_scripts:
64+
- echo `Installed my awesome app`
65+
postuninstall_scripts:
66+
- echo `Surprised Pickachu face`
67+
68+
# application icon path relative to project url
69+
icon: assets/logo.png
70+
71+
keywords:
72+
- Hello
73+
- World
74+
- Test
75+
- Application
76+
77+
# a name to categorize the app into a section of application
78+
generic_name: Music Application
79+
80+
# supported mime types that can be opened using this application
81+
# supported_mime_type:
82+
# - audio/mpeg
83+
84+
metainfo: linux/packaging/helloworld.appdata.xml
85+
86+
# shown when right clicked the desktop entry icons
87+
# actions:
88+
# - Gallery
89+
# - Create
90+
91+
# the categories the application belong to
92+
# refer: https://specifications.freedesktop.org/menu-spec/latest/
93+
categories:
94+
- Music
95+
- Media
96+
97+
# let OS know if the application can be run on start_up. If it's false
98+
# the application will deny to the OS if it was added as a start_up
99+
# application
100+
startup_notify: true

packages/flutter_app_builder/pubspec.yaml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@ dependencies:
1313
pub_semver: ^2.1.0
1414
pubspec_parse: ^1.1.0
1515
recase: ^4.1.0
16-
shell_executor: ^0.1.5
16+
shell_executor:
17+
path: ../shell_executor
1718

1819
dev_dependencies:
1920
dependency_validator: ^3.0.0

packages/flutter_app_packager/lib/src/flutter_app_packager.dart

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import 'dart:io';
22

33
import 'package:flutter_app_packager/src/api/app_package_maker.dart';
44
import 'package:flutter_app_packager/src/makers/makers.dart';
5+
import 'package:flutter_app_packager/src/makers/pacman/app_package_maker_pacman.dart';
56

67
class FlutterAppPackager {
78
final List<AppPackageMaker> _makers = [
@@ -18,6 +19,7 @@ class FlutterAppPackager {
1819
AppPackageMakerMsix(),
1920
AppPackageMakerPkg(),
2021
AppPackageMakerRPM(),
22+
AppPackageMakerPacman(),
2123
AppPackageMakerZip('linux'),
2224
AppPackageMakerZip('macos'),
2325
AppPackageMakerZip('windows'),
Lines changed: 197 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,197 @@
1+
import 'dart:io';
2+
3+
import 'package:flutter_app_packager/src/api/app_package_maker.dart';
4+
import 'package:flutter_app_packager/src/makers/pacman/make_pacman_config.dart';
5+
import 'package:path/path.dart' as path;
6+
import 'package:shell_executor/shell_executor.dart';
7+
8+
class AppPackageMakerPacman extends AppPackageMaker {
9+
@override
10+
String get name => 'pacman';
11+
@override
12+
String get platform => 'linux';
13+
@override
14+
bool get isSupportedOnCurrentPlatform => Platform.isLinux;
15+
@override
16+
String get packageFormat => 'pacman';
17+
18+
@override
19+
MakeConfigLoader get configLoader {
20+
return MakePacmanConfigLoader()
21+
..platform = platform
22+
..packageFormat = packageFormat;
23+
}
24+
25+
@override
26+
Future<MakeResult> make(MakeConfig config) {
27+
return _make(
28+
config.buildOutputDirectory,
29+
outputDirectory: config.outputDirectory,
30+
makeConfig: config as MakePacmanConfig,
31+
);
32+
}
33+
34+
Future<MakeResult> _make(
35+
Directory appDirectory, {
36+
required Directory outputDirectory,
37+
required MakePacmanConfig makeConfig,
38+
}) async {
39+
final files = makeConfig.toFilesString();
40+
41+
Directory packagingDirectory = makeConfig.packagingDirectory;
42+
43+
/// Need to create following directories
44+
/// /usr/share/$appBinaryName
45+
/// /usr/share/applications
46+
/// /usr/share/icons/hicolor/128x128/apps
47+
/// /usr/share/icons/hicolor/256x256/apps
48+
49+
final applicationsDir =
50+
path.join(packagingDirectory.path, 'usr/share/applications');
51+
final icon128Dir = path.join(
52+
packagingDirectory.path,
53+
'usr/share/icons/hicolor/128x128/apps',
54+
);
55+
final icon256Dir = path.join(
56+
packagingDirectory.path,
57+
'usr/share/icons/hicolor/256x256/apps',
58+
);
59+
final metainfoDir =
60+
path.join(packagingDirectory.path, 'usr/share/metainfo');
61+
final mkdirProcessResult = await $('mkdir', [
62+
'-p',
63+
path.join(packagingDirectory.path, 'usr/share', makeConfig.appBinaryName),
64+
applicationsDir,
65+
if (makeConfig.metainfo != null) metainfoDir,
66+
if (makeConfig.icon != null) ...[icon128Dir, icon256Dir],
67+
]);
68+
69+
if (mkdirProcessResult.exitCode != 0) throw MakeError();
70+
71+
if (makeConfig.icon != null) {
72+
final iconFile = File(makeConfig.icon!);
73+
if (!iconFile.existsSync()) {
74+
throw MakeError("provided icon ${makeConfig.icon} path wasn't found");
75+
}
76+
77+
await iconFile.copy(
78+
path.join(
79+
icon128Dir,
80+
makeConfig.appBinaryName + path.extension(makeConfig.icon!),
81+
),
82+
);
83+
await iconFile.copy(
84+
path.join(
85+
icon256Dir,
86+
makeConfig.appBinaryName + path.extension(makeConfig.icon!),
87+
),
88+
);
89+
}
90+
if (makeConfig.metainfo != null) {
91+
final metainfoPath =
92+
path.join(Directory.current.path, makeConfig.metainfo!);
93+
final metainfoFile = File(metainfoPath);
94+
if (!metainfoFile.existsSync()) {
95+
throw MakeError("Metainfo $metainfoPath path wasn't found");
96+
}
97+
await metainfoFile.copy(
98+
path.join(
99+
metainfoDir,
100+
makeConfig.appBinaryName + path.extension(makeConfig.metainfo!, 2),
101+
),
102+
);
103+
}
104+
105+
// create & write the files got from makeConfig
106+
final installFile = File(path.join(packagingDirectory.path, '.INSTALL'));
107+
final pkgInfoFile = File(path.join(packagingDirectory.path, '.PKGINFO'));
108+
final desktopEntryFile =
109+
File(path.join(applicationsDir, '${makeConfig.appBinaryName}.desktop'));
110+
111+
if (!installFile.existsSync()) installFile.createSync();
112+
if (!pkgInfoFile.existsSync()) pkgInfoFile.createSync();
113+
if (!desktopEntryFile.existsSync()) desktopEntryFile.createSync();
114+
115+
await installFile.writeAsString(files['INSTALL']!);
116+
await pkgInfoFile.writeAsString(files['PKGINFO']!);
117+
await desktopEntryFile.writeAsString(files['DESKTOP']!);
118+
119+
// copy the application binary to /usr/share/$appBinaryName
120+
await $('cp', [
121+
'-fr',
122+
'${appDirectory.path}/.',
123+
'${packagingDirectory.path}/usr/share/${makeConfig.appBinaryName}/',
124+
]);
125+
126+
// MTREE Metadata using bsdtar and fakeroot
127+
ProcessResult mtreeResult = await $(
128+
'bsdtar',
129+
[
130+
'-czf',
131+
'.MTREE',
132+
'--format=mtree',
133+
'--options=!all,use-set,type,uid,gid,mode,time,size,md5,sha256,link',
134+
'.PKGINFO',
135+
'.INSTALL',
136+
'usr',
137+
],
138+
environment: {
139+
'LANG': 'C',
140+
},
141+
workingDirectory: packagingDirectory.path,
142+
);
143+
if (mtreeResult.exitCode != 0) {
144+
throw MakeError(mtreeResult.stderr);
145+
}
146+
147+
// create the pacman package using fakeroot and bsdtar
148+
// fakeroot -- env LANG=C bsdtar -cf - .MTREE .PKGINFO * | xz -c -z - > $pkgname-$pkgver-$pkgrel-$arch.tar.xz
149+
150+
ProcessResult archiveResult = await $(
151+
'bsdtar',
152+
[
153+
'-cf',
154+
'temptar',
155+
'.MTREE',
156+
'.INSTALL',
157+
'.PKGINFO',
158+
'usr',
159+
],
160+
environment: {
161+
'LANG': 'C',
162+
},
163+
workingDirectory: packagingDirectory.path,
164+
);
165+
if (archiveResult.exitCode != 0) {
166+
throw MakeError(archiveResult.stderr);
167+
}
168+
169+
ProcessResult processResult = await $(
170+
'xz',
171+
[
172+
'-z',
173+
'temptar',
174+
],
175+
workingDirectory: packagingDirectory.path,
176+
);
177+
178+
if (processResult.exitCode != 0) {
179+
throw MakeError(processResult.stderr);
180+
}
181+
182+
// copy file from temptar.xz to the makeConfig.outputFile.path
183+
final copyResult = await $(
184+
'mv',
185+
[
186+
'${packagingDirectory.path}/temptar.xz',
187+
makeConfig.outputFile.path,
188+
],
189+
);
190+
if (copyResult.exitCode != 0) {
191+
throw MakeError(copyResult.stderr);
192+
}
193+
194+
packagingDirectory.deleteSync(recursive: true);
195+
return MakeResult(makeConfig);
196+
}
197+
}

0 commit comments

Comments
 (0)