-
Notifications
You must be signed in to change notification settings - Fork 30
/
ph_pacman.ml
249 lines (224 loc) · 7.69 KB
/
ph_pacman.ml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
(* supermin 5
* Copyright (C) 2009-2014 Red Hat Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*)
open Unix
open Unix.LargeFile
open Printf
open Utils
open Package_handler
let pacman_detect () =
Config.pacman <> "no" && Config.fakeroot <> "no" &&
(Os_release.get_id () = "arch" ||
Os_release.get_id () = "artix" ||
((stat "/etc/arch-release").st_kind = S_REG &&
Config.pacman_g2 = "no")) (* not Frugalware with pacman-g2 *)
let settings = ref no_settings
let pacman_init s = settings := s
type pac_t = {
name : string;
epoch : int;
version : string;
release : string;
arch : string;
}
(* Memo from package type to internal pac_t. *)
let pac_of_pkg, pkg_of_pac = get_memo_functions ()
(* Memo of pacman_package_of_string. *)
let pach = Hashtbl.create 13
let pacman_package_of_string str =
(* Parse a package name into the fields like name and version. *)
let parse_pac str =
let cmd = sprintf "%s -Qi %s" Config.pacman (quote str) in
if !settings.debug >= 2 then printf "%s" cmd;
let lines = run_command_get_lines cmd in
let name = ref "" and evr = ref "" and arch = ref "" in
List.iter (
fun line ->
let get_value r =
let len = String.length line in
let i = String.index line ':' in
r := String.sub line (i+2) (len-(i+2))
in
if string_prefix "Name " line then get_value name
else if string_prefix "Version " line then get_value evr
else if string_prefix "Architecture " line then get_value arch
) lines;
let name = !name and evr = !evr and arch = !arch in
if name = "" || evr = "" || arch = "" then
error "pacman: Name/Version/Architecture field missing in output of %s" cmd;
(* Parse epoch:version-release field. *)
let epoch, version, release =
try
let epoch, vr =
try
let i = String.index evr ':' in
int_of_string (String.sub evr 0 i),
String.sub evr (i+1) (String.length evr - (i+1))
with Not_found -> 0, evr in
let version, release =
match string_split "-" vr with
| [ v; r ] -> v, r
| _ -> assert false in
epoch, version, release
with
Failure _ ->
error "failed to parse epoch:version-release field: %s " evr in
{ name = name;
epoch = epoch;
version = version;
release = release;
arch = arch }
(* Check if a package is installed. *)
and check_pac_installed name =
let cmd = sprintf "%s -Qq %s >/dev/null 2>&1" Config.pacman (quote name) in
if !settings.debug >= 2 then printf "%s" cmd;
0 = Sys.command cmd
in
try
Hashtbl.find pach str
with
Not_found ->
let r =
if check_pac_installed str then (
let pac = parse_pac str in
Some (pkg_of_pac pac)
)
else None in
Hashtbl.add pach str r;
r
let pacman_package_to_string pkg =
let pac = pac_of_pkg pkg in
if pac.epoch = 0 then
sprintf "%s-%s-%s.%s" pac.name pac.version pac.release pac.arch
else
sprintf "%s-%d:%s-%s.%s"
pac.name pac.epoch pac.version pac.release pac.arch
let pacman_package_name pkg =
let pac = pac_of_pkg pkg in
pac.name
let pacman_get_package_database_mtime () =
(* This directory changes mtime when packages get installed/reinstalled.
* The directory itself contains several *.db files with different names.
*)
(lstat "/var/lib/pacman/sync/").st_mtime
let pacman_get_all_requires pkgs =
let cmd = sprintf "\
for p in %s; do %s -u $p; done | awk '{print $1}' | sort -u
" (quoted_list (List.map pacman_package_name (PackageSet.elements pkgs)))
Config.pactree in
if !settings.debug >= 2 then printf "%s" cmd;
let lines = run_command_get_lines cmd in
let lines = filter_map pacman_package_of_string lines in
PackageSet.union pkgs (package_set_of_list lines)
let pacman_get_all_files pkgs =
let cmd =
sprintf "%s -Ql %s | awk '{print $2}'"
Config.pacman
(quoted_list (List.map pacman_package_name (PackageSet.elements pkgs))) in
if !settings.debug >= 2 then printf "%s" cmd;
let lines = run_command_get_lines cmd in
List.map (
fun path ->
(* Remove trailing / from directory names. *)
let path =
let len = String.length path in
if len >= 2 && path.[len-1] = '/' then
String.sub path 0 (len-1)
else
path in
let config =
try string_prefix "/etc/" path && (lstat path).st_kind = S_REG
with Unix_error _ -> false in
{ ft_path = path; ft_source_path = path; ft_config = config }
) lines
let pacman_download_all_packages pkgs dir =
let tdir = !settings.tmpdir // string_random8 () in
mkdir tdir 0o755;
let names = List.map pacman_package_name (PackageSet.elements pkgs) in
(* Because we reuse the same temporary download directory (tdir), this
* only downloads each package once, even though each call to pacman will
* download dependent packages as well.
*
* CacheDir directives must be filtered out to force pacman downloads.
*)
let cmd = sprintf "\
set -e
umask 0000
cd %s
mkdir -p var/lib/pacman
pacman-conf | grep -v CacheDir > tmp.conf
%s %s%s -Syw --noconfirm --cachedir=$(pwd) --root=$(pwd) %s
"
(quote tdir)
Config.fakeroot Config.pacman
(match !settings.packager_config with
| None -> " --config tmp.conf --dbpath var/lib/pacman"
| Some filename -> " --config " ^ (quote filename))
(quoted_list names) in
if !settings.debug >= 2 then printf "%s" cmd;
if Sys.command cmd <> 0 then (
(* The package may not be in the main repos, check the AUR. *)
List.iter (
fun name ->
let cmd = sprintf "\
set -e
umask 0000
cd %s
wget %s
tar xf %s
cd %s
%s
mv %s-*.pkg.tar.xz %s
"
(quote tdir)
(quote ("https://aur.archlinux.org/packages/" ^
(String.sub name 0 2) ^
"/" ^ name ^ "/" ^ name ^ ".tar.gz"))
(quote (name ^ ".tar.gz"))
(quote name) (* cd *)
Config.makepkg
(quote name) (quote tdir) (* mv *) in
if !settings.debug >= 2 then printf "%s" cmd;
run_command cmd
) names;
);
(* Unpack the downloaded packages. *)
let cmd =
sprintf "
umask 0000
for f in %s/*.pkg.tar.*; do
[[ $f == *.sig ]] && continue
tar -xf \"$f\" -C %s
done
"
(quote tdir) (quote dir) in
if !settings.debug >= 2 then printf "%s" cmd;
run_command cmd
let () =
let ph = {
ph_detect = pacman_detect;
ph_init = pacman_init;
ph_fini = (fun () -> ());
ph_package_of_string = pacman_package_of_string;
ph_package_to_string = pacman_package_to_string;
ph_package_name = pacman_package_name;
ph_get_package_database_mtime = pacman_get_package_database_mtime;
ph_get_requires = PHGetAllRequires pacman_get_all_requires;
ph_get_files = PHGetAllFiles pacman_get_all_files;
ph_download_package = PHDownloadAllPackages pacman_download_all_packages;
} in
register_package_handler "arch" "pacman" ph