]>
Commit | Line | Data |
---|---|---|
1a276007 MT |
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 <stdlib.h> | |
23 | ||
24 | #include <pakfire/build.h> | |
25 | #include <pakfire/execute.h> | |
26 | #include <pakfire/dist.h> | |
27 | #include <pakfire/logging.h> | |
4c07774f | 28 | #include <pakfire/package.h> |
48c6f2e7 | 29 | #include <pakfire/packager.h> |
1a276007 MT |
30 | #include <pakfire/parser.h> |
31 | #include <pakfire/private.h> | |
32 | #include <pakfire/types.h> | |
33 | #include <pakfire/util.h> | |
34 | ||
35 | static const char* stages[] = { | |
36 | "prepare", | |
37 | "build", | |
38 | "test", | |
39 | "install", | |
40 | NULL, | |
41 | }; | |
42 | ||
43 | #define TEMPLATE \ | |
44 | "#!/bin/bash --login\n" \ | |
45 | "\n" \ | |
46 | "set -e\n" \ | |
47 | "set -x\n" \ | |
48 | "\n" \ | |
49 | "%%{_%s}\n" \ | |
50 | "\n" \ | |
51 | "exit 0\n" | |
52 | ||
73543ae3 MT |
53 | static int append_to_array(const char*** array, const char* s) { |
54 | unsigned int length = 0; | |
55 | ||
56 | // Determine the length of the existing array | |
57 | if (*array) { | |
58 | for (const char** element = *array; *element; element++) | |
59 | length++; | |
60 | } | |
61 | ||
62 | // Allocate space | |
63 | *array = reallocarray(*array, length + 2, sizeof(**array)); | |
64 | if (!*array) | |
65 | return 1; | |
66 | ||
67 | // Append s and terminate the array | |
68 | (*array)[length] = s; | |
69 | (*array)[length + 1] = NULL; | |
70 | ||
71 | return 0; | |
72 | } | |
73 | ||
74 | static int pakfire_build_package_add_files(Pakfire pakfire, PakfireParser makefile, | |
75 | const char* namespace, struct pakfire_packager* packager) { | |
76 | PakfireFilelist filelist = NULL; | |
77 | char path[PATH_MAX]; | |
78 | int r = 1; | |
79 | ||
80 | char** files = NULL; | |
81 | const char** includes = NULL; | |
82 | const char** excludes = NULL; | |
83 | ||
84 | // Fetch filelist from makefile | |
85 | files = pakfire_parser_get_split(makefile, namespace, "files", '\n'); | |
86 | ||
87 | // No files to package? | |
88 | if (!files) | |
89 | return 0; | |
90 | ||
91 | // Fetch buildroot | |
92 | char* buildroot = pakfire_parser_get(makefile, NULL, "BUILDROOT"); | |
93 | if (!buildroot) | |
94 | goto ERROR; | |
95 | ||
96 | // Convert to absolute path | |
97 | pakfire_make_path(pakfire, path, buildroot); | |
98 | ||
99 | // Split into includes and excludes | |
100 | for (char** file = files; *file; file++) { | |
101 | if (**file == '!') | |
102 | r = append_to_array(&excludes, *file); | |
103 | else | |
104 | r = append_to_array(&includes, *file); | |
105 | if (r) | |
106 | goto ERROR; | |
107 | } | |
108 | ||
109 | // Allocate a new filelist | |
110 | r = pakfire_filelist_create(&filelist, pakfire); | |
111 | if (r) | |
112 | goto ERROR; | |
113 | ||
114 | // Scan for files | |
115 | r = pakfire_filelist_scan(filelist, path, includes, excludes); | |
116 | if (r) | |
117 | goto ERROR; | |
118 | ||
119 | size_t length = pakfire_filelist_size(filelist); | |
120 | DEBUG(pakfire, "%zu file(s) found\n", length); | |
121 | ||
122 | // XXX package everything | |
123 | ||
124 | ERROR: | |
125 | if (filelist) | |
126 | pakfire_filelist_unref(filelist); | |
127 | if (files) { | |
128 | for (char** file = files; *file; file++) | |
129 | free(*file); | |
130 | free(files); | |
131 | } | |
132 | if (includes) | |
133 | free(includes); | |
134 | if (excludes) | |
135 | free(excludes); | |
136 | if (buildroot) | |
137 | free(buildroot); | |
138 | ||
139 | return r; | |
140 | } | |
141 | ||
48c6f2e7 MT |
142 | static int pakfire_build_package(Pakfire pakfire, PakfireParser makefile, |
143 | const char* namespace, const char* target) { | |
4c07774f MT |
144 | PakfireRepo repo = NULL; |
145 | PakfirePackage pkg = NULL; | |
48c6f2e7 | 146 | struct pakfire_packager* packager = NULL; |
73543ae3 | 147 | |
a50bde9c MT |
148 | int r = 1; |
149 | ||
150 | // Expand the handle into the package name | |
4c07774f | 151 | char* name = pakfire_parser_expand(makefile, "packages", namespace); |
a50bde9c MT |
152 | if (!name) { |
153 | ERROR(pakfire, "Could not get package name: %s\n", strerror(errno)); | |
154 | goto ERROR; | |
155 | } | |
156 | ||
157 | INFO(pakfire, "Building package '%s'...\n", name); | |
158 | ||
4c07774f MT |
159 | // Fetch build architecture |
160 | const char* arch = pakfire_get_arch(pakfire); | |
161 | if (!arch) | |
162 | goto ERROR; | |
163 | ||
164 | // Fetch the dummy repository | |
165 | repo = pakfire_get_repo(pakfire, "@dummy"); | |
166 | if (!repo) | |
167 | goto ERROR; | |
168 | ||
169 | // Fetch package from makefile | |
170 | r = pakfire_parser_create_package(makefile, &pkg, repo, namespace, arch); | |
171 | if (r) { | |
172 | ERROR(pakfire, "Could not create package from makefile: %s\n", strerror(errno)); | |
173 | goto ERROR; | |
174 | } | |
175 | ||
48c6f2e7 MT |
176 | // Create a packager |
177 | r = pakfire_packager_create(&packager, pkg); | |
178 | if (r) | |
179 | goto ERROR; | |
180 | ||
73543ae3 MT |
181 | // Add files |
182 | r = pakfire_build_package_add_files(pakfire, makefile, namespace, packager); | |
183 | if (r) | |
184 | goto ERROR; | |
185 | ||
4c07774f | 186 | #ifdef ENABLE_DEBUG |
23e22751 | 187 | char* dump = pakfire_package_dump(pkg, PAKFIRE_PKG_DUMP_LONG); |
4c07774f MT |
188 | if (dump) { |
189 | DEBUG(pakfire, "%s\n", dump); | |
190 | free(dump); | |
191 | } | |
192 | #endif | |
a50bde9c | 193 | |
48c6f2e7 MT |
194 | // Write the finished package |
195 | r = pakfire_packager_finish_to_directory(packager, target); | |
196 | if (r) { | |
197 | ERROR(pakfire, "pakfire_packager_finish() failed: %s\n", strerror(errno)); | |
198 | goto ERROR; | |
199 | } | |
200 | ||
a50bde9c MT |
201 | // Success |
202 | r = 0; | |
203 | ||
204 | ERROR: | |
48c6f2e7 MT |
205 | if (packager) |
206 | pakfire_packager_unref(packager); | |
4c07774f MT |
207 | if (repo) |
208 | pakfire_repo_unref(repo); | |
209 | if (pkg) | |
210 | pakfire_package_unref(pkg); | |
a50bde9c MT |
211 | if (name) |
212 | free(name); | |
213 | ||
214 | return r; | |
215 | } | |
216 | ||
48c6f2e7 | 217 | static int pakfire_build_packages(Pakfire pakfire, PakfireParser makefile, const char* target) { |
a50bde9c MT |
218 | DEBUG(pakfire, "Creating packages..."); |
219 | int r = 1; | |
220 | ||
221 | // Fetch a list all all packages | |
222 | char** packages = pakfire_parser_list_namespaces(makefile, "packages.package:*"); | |
223 | if (!packages) { | |
224 | ERROR(pakfire, "Could not find any packages: %s\n", strerror(errno)); | |
225 | goto ERROR; | |
226 | } | |
227 | ||
228 | unsigned int num_packages = 0; | |
229 | ||
230 | // Count how many packages we have | |
231 | for (char** package = packages; *package; package++) | |
232 | num_packages++; | |
233 | ||
234 | DEBUG(pakfire, "Found %d package(s)\n", num_packages); | |
235 | ||
236 | // Build packages in reverse order | |
237 | for (int i = num_packages - 1; i >= 0; i--) { | |
48c6f2e7 | 238 | r = pakfire_build_package(pakfire, makefile, packages[i], target); |
a50bde9c MT |
239 | if (r) |
240 | goto ERROR; | |
241 | } | |
242 | ||
243 | // Success | |
244 | r = 0; | |
245 | ||
246 | ERROR: | |
247 | if (packages) | |
248 | free(packages); | |
249 | ||
250 | return r; | |
251 | } | |
252 | ||
7d999600 MT |
253 | static int pakfire_build_stage(Pakfire pakfire, PakfireParser makefile, const char* stage, |
254 | pakfire_execute_logging_callback logging_callback, void* data) { | |
1a276007 MT |
255 | char template[1024]; |
256 | ||
257 | // Prepare template for this stage | |
258 | int r = pakfire_string_format(template, TEMPLATE, stage); | |
259 | if (r < 0) | |
260 | return r; | |
261 | ||
262 | // Create the build script | |
263 | char* script = pakfire_parser_expand(makefile, "build", template); | |
264 | if (!script) { | |
265 | ERROR(pakfire, "Could not generate the build script for stage '%s': %s\n", | |
266 | stage, strerror(errno)); | |
267 | goto ERROR; | |
268 | } | |
269 | ||
270 | INFO(pakfire, "Running build stage '%s'\n", stage); | |
271 | ||
7d999600 MT |
272 | r = pakfire_execute_script(pakfire, script, strlen(script), NULL, 0, |
273 | logging_callback, data); | |
1a276007 MT |
274 | if (r) { |
275 | ERROR(pakfire, "Build stage '%s' failed with status %d\n", stage, r); | |
276 | } | |
277 | ||
278 | ERROR: | |
279 | if (script) | |
280 | free(script); | |
281 | ||
282 | return r; | |
283 | } | |
284 | ||
285 | PAKFIRE_EXPORT int pakfire_build(Pakfire pakfire, const char* path, | |
7d999600 MT |
286 | const char* target, int flags, |
287 | pakfire_execute_logging_callback logging_callback, void* data) { | |
1a276007 MT |
288 | PakfireParser makefile = NULL; |
289 | struct pakfire_parser_error* error = NULL; | |
290 | ||
48c6f2e7 MT |
291 | // Check for valid input |
292 | if (!path) { | |
293 | errno = EINVAL; | |
294 | return 1; | |
295 | } | |
296 | ||
297 | // The default target is the local repository path | |
298 | if (!target) { | |
299 | PakfireRepo repo = pakfire_get_repo(pakfire, "local"); | |
300 | if (!repo) { | |
301 | errno = EINVAL; | |
302 | return 1; | |
303 | } | |
304 | ||
305 | target = pakfire_repo_get_path(repo); | |
306 | pakfire_repo_unref(repo); | |
307 | } | |
308 | ||
1a276007 MT |
309 | // Read makefile |
310 | int r = pakfire_read_makefile(&makefile, pakfire, path, &error); | |
311 | if (r) { | |
312 | if (error) { | |
313 | ERROR(pakfire, "Could not parse makefile %s: %s\n", path, | |
314 | pakfire_parser_error_get_message(error)); | |
315 | pakfire_parser_error_unref(error); | |
316 | } else { | |
317 | ERROR(pakfire, "Could not parse makefile %s: %s\n", path, | |
318 | strerror(errno)); | |
319 | } | |
320 | ||
321 | goto ERROR; | |
322 | } | |
323 | ||
324 | // Run through all build stages | |
325 | for (const char** stage = stages; *stage; stage++) { | |
7d999600 | 326 | int r = pakfire_build_stage(pakfire, makefile, *stage, logging_callback, data); |
1a276007 MT |
327 | if (r) { |
328 | // Drop to a shell for debugging | |
329 | if (flags & PAKFIRE_BUILD_INTERACTIVE) | |
330 | pakfire_shell(pakfire); | |
331 | ||
332 | goto ERROR; | |
333 | } | |
334 | } | |
335 | ||
a50bde9c | 336 | // Create the packages |
48c6f2e7 | 337 | r = pakfire_build_packages(pakfire, makefile, target); |
a50bde9c MT |
338 | if (r) { |
339 | ERROR(pakfire, "Could not create packages: %s\n", strerror(errno)); | |
340 | goto ERROR; | |
341 | } | |
1a276007 MT |
342 | |
343 | ERROR: | |
344 | if (makefile) | |
345 | pakfire_parser_unref(makefile); | |
346 | ||
347 | return r; | |
348 | } | |
349 | ||
350 | PAKFIRE_EXPORT int pakfire_shell(Pakfire pakfire) { | |
351 | const char* argv[] = { | |
352 | "/bin/bash", "--login", NULL, | |
353 | }; | |
354 | ||
355 | const int flags = | |
356 | PAKFIRE_EXECUTE_INTERACTIVE | PAKFIRE_EXECUTE_ENABLE_NETWORK; | |
357 | ||
358 | return pakfire_execute(pakfire, argv, NULL, flags, NULL, NULL); | |
359 | } |