]> git.ipfire.org Git - pakfire.git/blob - src/libpakfire/dist.c
1d249bef14e8261419678940044d6412faea387b
[pakfire.git] / src / libpakfire / dist.c
1 /*#############################################################################
2 # #
3 # Pakfire - The IPFire package management system #
4 # Copyright (C) 2021 Pakfire development team #
5 # #
6 # This program is free software: you can redistribute it and/or modify #
7 # it under the terms of the GNU General Public License as published by #
8 # the Free Software Foundation, either version 3 of the License, or #
9 # (at your option) any later version. #
10 # #
11 # This program is distributed in the hope that it will be useful, #
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of #
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
14 # GNU General Public License for more details. #
15 # #
16 # You should have received a copy of the GNU General Public License #
17 # along with this program. If not, see <http://www.gnu.org/licenses/>. #
18 # #
19 #############################################################################*/
20
21 #include <errno.h>
22 #include <fts.h>
23 #include <fcntl.h>
24 #include <glob.h>
25 #include <stddef.h>
26 #include <stdlib.h>
27
28 #include <pakfire/arch.h>
29 #include <pakfire/dist.h>
30 #include <pakfire/downloader.h>
31 #include <pakfire/logging.h>
32 #include <pakfire/package.h>
33 #include <pakfire/packager.h>
34 #include <pakfire/pakfire.h>
35 #include <pakfire/parser.h>
36 #include <pakfire/private.h>
37 #include <pakfire/repo.h>
38 #include <pakfire/string.h>
39 #include <pakfire/util.h>
40
41 // XXX for now
42 #define BASEURL "https://source.ipfire.org/source-3.x/"
43
44 #define PAKFIRE_MACROS_DIR "/usr/lib/pakfire/macros"
45 #define PAKFIRE_MACROS_GLOB_PATTERN PAKFIRE_MACROS_DIR "/*.macro"
46
47 static const char* pakfire_dist_excludes[] = {
48 // Don't package any backup files
49 "*~",
50 "*.bak",
51
52 // Don't package any other package files
53 "*.pfm",
54
55 NULL,
56 };
57
58 static int pakfire_makefile_set_defaults(struct pakfire* pakfire,
59 struct pakfire_parser* parser, const char* path) {
60 char buffer[1024];
61 int r;
62
63 // Set epoch
64 pakfire_parser_set(parser, NULL, "epoch", "0", 0);
65
66 // Set EVR
67 pakfire_parser_set(parser, NULL, "evr", "%{epoch}:%{version}-%{_release}", 0);
68
69 // Set vendor
70 pakfire_parser_set(parser, NULL, "vendor", "%{DISTRO_VENDOR}", 0);
71
72 // Set DISTRO_NAME
73 const char* name = pakfire_get_distro_name(pakfire);
74 if (name)
75 pakfire_parser_set(parser, NULL, "DISTRO_NAME", name, 0);
76
77 // Set DISTRO_SNAME
78 const char* id = pakfire_get_distro_id(pakfire);
79 if (id)
80 pakfire_parser_set(parser, NULL, "DISTRO_SNAME", name, 0);
81
82 // Set DISTRO_RELEASE
83 const char* version_id = pakfire_get_distro_version_id(pakfire);
84 if (version_id)
85 pakfire_parser_set(parser, NULL, "DISTRO_RELEASE", version_id, 0);
86
87 // Set DISTRO_DISTTAG
88 const char* tag = pakfire_get_distro_tag(pakfire);
89 if (tag)
90 pakfire_parser_set(parser, NULL, "DISTRO_DISTTAG", tag, 0);
91
92 // Set DISTRO_VENDOR
93 const char* vendor = pakfire_get_distro_vendor(pakfire);
94 if (vendor)
95 pakfire_parser_set(parser, NULL, "DISTRO_VENDOR", vendor, 0);
96
97 // Set DISTRO_ARCH
98 const char* arch = pakfire_get_arch(pakfire);
99 if (arch) {
100 pakfire_parser_set(parser, NULL, "DISTRO_ARCH", arch, 0);
101
102 const char* platform = pakfire_arch_platform(arch);
103 if (platform)
104 pakfire_parser_set(parser, NULL, "DISTRO_PLATFORM", platform, 0);
105
106 if (vendor) {
107 // Set DISTRO_MACHINE
108 r = pakfire_arch_machine(buffer, arch, vendor);
109 if (!r)
110 pakfire_parser_set(parser, NULL, "DISTRO_MACHINE", buffer, 0);
111
112 // Set DISTRO_BUILDTARGET
113 r = pakfire_arch_buildtarget(buffer, arch, vendor);
114 if (!r)
115 pakfire_parser_set(parser, NULL, "DISTRO_BUILDTARGET", buffer, 0);
116 }
117 }
118
119 // Set BASEDIR
120 const char* dirname = pakfire_dirname(path);
121 if (dirname) {
122 const char* root = pakfire_get_path(pakfire);
123
124 pakfire_parser_set(parser, NULL, "BASEDIR",
125 pakfire_path_relpath(root, dirname), 0);
126 }
127
128 long processors_online = sysconf(_SC_NPROCESSORS_ONLN);
129
130 // Set PARALLELISMFLAGS
131 if (processors_online) {
132 pakfire_string_format(buffer, "-j%ld", processors_online);
133 pakfire_parser_set(parser, "build", "PARALLELISMFLAGS", buffer, 0);
134 }
135
136 return 0;
137 }
138
139 int pakfire_read_makefile(struct pakfire_parser** parser, struct pakfire* pakfire,
140 const char* path, struct pakfire_parser_error** error) {
141 int r = 1;
142
143 *parser = pakfire_parser_create(pakfire, NULL, NULL, PAKFIRE_PARSER_FLAGS_EXPAND_COMMANDS);
144 if (!*parser) {
145 r = 1;
146 goto ERROR;
147 }
148
149 // Set defaults
150 r = pakfire_makefile_set_defaults(pakfire, *parser, path);
151 if (r)
152 goto ERROR;
153
154 // Find all macros
155
156 DEBUG(pakfire, "Searching for macros in %s\n", PAKFIRE_MACROS_GLOB_PATTERN);
157
158 glob_t globmacros;
159 r = glob(PAKFIRE_MACROS_GLOB_PATTERN, 0, NULL, &globmacros);
160
161 // Handle any errors
162 switch (r) {
163 case 0:
164 case GLOB_NOMATCH:
165 break;
166
167 case GLOB_NOSPACE:
168 errno = ENOMEM;
169 goto ERROR;
170
171 case GLOB_ABORTED:
172 goto ERROR;
173
174 default:
175 ERROR(pakfire, "glob() returned an unhandled error: %d\n", r);
176 goto ERROR;
177 }
178
179 DEBUG(pakfire, "Found %zu macro(s)\n", globmacros.gl_pathc);
180
181 // Read all macros
182 for (unsigned int i = 0; i < globmacros.gl_pathc; i++) {
183 // Parse the file
184 r = pakfire_parser_read_file(*parser, globmacros.gl_pathv[i], error);
185 if (r)
186 goto ERROR;
187 }
188
189 globfree(&globmacros);
190
191 // Finally, parse the makefile
192 r = pakfire_parser_read_file(*parser, path, error);
193 if (r) {
194 ERROR(pakfire, "Could not read makefile %s: %m\n", path);
195 goto ERROR;
196 }
197
198 return 0;
199
200 ERROR:
201 globfree(&globmacros);
202
203 if (*parser) {
204 pakfire_parser_unref(*parser);
205 *parser = NULL;
206 }
207
208 return r;
209 }
210
211 static int pakfire_dist_create_downloader_and_mirrorlist(
212 struct pakfire* pakfire, struct pakfire_parser* makefile,
213 struct pakfire_downloader** downloader, struct pakfire_mirrorlist** mirrorlist) {
214 char* p = NULL;
215
216 // Create the downloader
217 int r = pakfire_downloader_create(downloader, pakfire);
218 if (r) {
219 ERROR(pakfire, "Could not initialize downloader\n");
220 return r;
221 }
222
223 // Fetch source_dl
224 char* source_dl = pakfire_parser_get(makefile, NULL, "source_dl");
225
226 // We do not need to create a mirrorlist if this isn't set
227 if (!source_dl)
228 return 0;
229
230 // Create mirrorlist
231 r = pakfire_mirrorlist_create(mirrorlist, pakfire);
232 if (r) {
233 ERROR(pakfire, "Could not create the mirrorlist\n");
234 goto ERROR;
235 }
236
237 // Add all mirrors
238 const char* mirror = strtok_r(source_dl, " ", &p);
239
240 while (mirror) {
241 r = pakfire_mirrorlist_add_mirror(*mirrorlist, mirror);
242 if (r)
243 goto ERROR;
244
245 mirror = strtok_r(NULL, " ", &p);
246 }
247
248 // Success
249 r = 0;
250
251 ERROR:
252 if (source_dl)
253 free(source_dl);
254
255 return r;
256 }
257
258 static int pakfire_dist_add_source(struct pakfire* pakfire, struct pakfire_packager* packager,
259 struct pakfire_package* pkg, struct pakfire_downloader* downloader,
260 struct pakfire_mirrorlist* mirrorlist, const char* filename) {
261 int r;
262 char archive_path[PATH_MAX];
263 char cache_path[PATH_MAX];
264
265 const char* name = pakfire_package_get_string(pkg, PAKFIRE_PKG_NAME);
266
267 // Compose path
268 r = pakfire_cache_path(pakfire, cache_path, "sources/%s/%s", name, filename);
269 if (r)
270 return r;
271
272 // Download the file if it does not exist in the cache
273 if (access(cache_path, R_OK) != 0) {
274 r = pakfire_downloader_retrieve(downloader, BASEURL, mirrorlist,
275 NULL, filename, cache_path, 0, NULL, 0, 0);
276 if (r)
277 return r;
278 }
279
280 pakfire_string_format(archive_path, "/files/%s", filename);
281
282 // Add file to package
283 return pakfire_packager_add(packager, cache_path, archive_path);
284 }
285
286 static int pakfire_dist_add_sources(struct pakfire* pakfire, struct pakfire_packager* packager,
287 struct pakfire_package* pkg, struct pakfire_parser* makefile) {
288 // Fetch sources
289 char** sources = pakfire_parser_get_split(makefile, NULL, "sources", ' ');
290
291 // Nothing to do if this variable is empty
292 if (!sources)
293 return 1;
294
295 struct pakfire_downloader* downloader = NULL;
296 struct pakfire_mirrorlist* mirrorlist = NULL;
297
298 // Create a downloader and mirrorlist
299 int r = pakfire_dist_create_downloader_and_mirrorlist(pakfire, makefile,
300 &downloader, &mirrorlist);
301 if (r)
302 goto ERROR;
303
304 // Add all files one by one
305 for (char** source = sources; *source; source++) {
306 DEBUG(pakfire, "Adding source file %s\n", *source);
307
308 r = pakfire_dist_add_source(pakfire, packager, pkg, downloader, mirrorlist, *source);
309 if (r) {
310 ERROR(pakfire, "Could not add '%s' to package: %m\n", *source);
311 goto ERROR;
312 }
313 }
314
315 // Success
316 r = 0;
317
318 ERROR:
319 if (downloader)
320 pakfire_downloader_unref(downloader);
321 if (mirrorlist)
322 pakfire_mirrorlist_unref(mirrorlist);
323
324 if (sources) {
325 for (char** source = sources; *source; source++)
326 free(*source);
327 free(sources);
328 }
329
330 return r;
331 }
332
333 static int pakfire_dist_add_files(struct pakfire* pakfire, struct pakfire_packager* packager,
334 const char* path) {
335 struct pakfire_filelist* filelist = NULL;
336 int r = 1;
337
338 // Find the parent directory
339 const char* dirname = pakfire_dirname(path);
340 if (!dirname)
341 return 1;
342
343 DEBUG(pakfire, "Adding all files in '%s' to package...\n", dirname);
344
345 // Create a new filelist
346 r = pakfire_filelist_create(&filelist, pakfire);
347 if (r)
348 goto ERROR;
349
350 // Scan for any files
351 r = pakfire_filelist_scan(filelist, dirname, NULL, pakfire_dist_excludes);
352 if (r)
353 goto ERROR;
354
355 // Add all files to the package
356 r = pakfire_packager_add_files(packager, filelist);
357 if (r)
358 goto ERROR;
359
360 ERROR:
361 if (filelist)
362 pakfire_filelist_unref(filelist);
363
364 return r;
365 }
366
367 PAKFIRE_EXPORT int pakfire_dist(struct pakfire* pakfire, const char* path,
368 const char* target, char** result) {
369 struct pakfire_parser* makefile;
370 struct pakfire_parser_error* error = NULL;
371
372 struct pakfire_packager* packager = NULL;
373 struct pakfire_package* pkg = NULL;
374
375 // Load makefile
376 int r = pakfire_read_makefile(&makefile, pakfire, path, &error);
377 if (r) {
378 if (error)
379 pakfire_parser_error_unref(error);
380 else
381 ERROR(pakfire, "Could not read makefile: %m\n");
382
383 return r;
384 }
385
386 // The architecture is always "src"
387 r = pakfire_parser_set(makefile, NULL, "arch", "src", 0);
388 if (r) {
389 ERROR(pakfire, "Could not set architecture to 'src': %m\n");
390 goto ERROR;
391 }
392
393 // Get the package object
394 r = pakfire_parser_create_package(makefile, &pkg, NULL, NULL, "src");
395 if (r)
396 goto ERROR;
397
398 // Create a packager
399 r = pakfire_packager_create(&packager, pakfire, pkg);
400 if (r)
401 goto ERROR;
402
403 // Add all files in the directory
404 r = pakfire_dist_add_files(pakfire, packager, path);
405 if (r)
406 goto ERROR;
407
408 // Add all source files (which might need to be downloaded)
409 r = pakfire_dist_add_sources(pakfire, packager, pkg, makefile);
410 if (r)
411 goto ERROR;
412
413 // Write the finished package
414 r = pakfire_packager_finish_to_directory(packager, target, result);
415 if (r)
416 goto ERROR;
417
418 ERROR:
419 if (packager)
420 pakfire_packager_unref(packager);
421 if (pkg)
422 pakfire_package_unref(pkg);
423 pakfire_parser_unref(makefile);
424
425 return r;
426 }