blob: b134e013c86e490f07bc8d269d6e879a0870de62 [file] [log] [blame]
The Android Open Source Project88b60792009-03-03 19:28:42 -08001#include <stdio.h>
Raphael0b3ec5d2011-09-14 15:07:05 -07002#include <stdlib.h>
The Android Open Source Project88b60792009-03-03 19:28:42 -08003#include <string.h>
4#include <unistd.h>
Raphael0b3ec5d2011-09-14 15:07:05 -07005#include <stdarg.h>
The Android Open Source Project88b60792009-03-03 19:28:42 -08006#include "options.h"
7#include "files.h"
8#include "fs.h"
9#include <set>
10#include <iostream>
11#include <sstream>
12
13using namespace std;
14
Raphael0b3ec5d2011-09-14 15:07:05 -070015bool g_debug = getenv("ATREE_DEBUG") != NULL;
The Android Open Source Project88b60792009-03-03 19:28:42 -080016vector<string> g_listFiles;
17vector<string> g_inputBases;
18map<string, string> g_variables;
19string g_outputBase;
20string g_dependency;
21bool g_useHardLinks = false;
22
23const char* USAGE =
24"\n"
25"Usage: atree OPTIONS\n"
26"\n"
27"Options:\n"
28" -f FILELIST Specify one or more files containing the\n"
29" list of files to copy.\n"
30" -I INPUTDIR Specify one or more base directories in\n"
31" which to look for the files\n"
32" -o OUTPUTDIR Specify the directory to copy all of the\n"
33" output files to.\n"
34" -l Use hard links instead of copying the files.\n"
35" -m DEPENDENCY Output a make-formatted file containing the list.\n"
36" of files included. It sets the variable ATREE_FILES.\n"
37" -v VAR=VAL Replaces ${VAR} by VAL when reading input files.\n"
Raphael0b3ec5d2011-09-14 15:07:05 -070038" -d Verbose debug mode.\n"
The Android Open Source Project88b60792009-03-03 19:28:42 -080039"\n"
40"FILELIST file format:\n"
41" The FILELIST files contain the list of files that will end up\n"
42" in the final OUTPUTDIR. Atree will look for files in the INPUTDIR\n"
43" directories in the order they are specified.\n"
44"\n"
45" In a FILELIST file, comment lines start with a #. Other lines\n"
46" are of the format:\n"
47"\n"
Raphael0b3ec5d2011-09-14 15:07:05 -070048" [rm|strip] DEST\n"
49" SRC [strip] DEST\n"
The Android Open Source Project88b60792009-03-03 19:28:42 -080050" -SRCPATTERN\n"
51"\n"
52" DEST should be path relative to the output directory.\n"
Raphael0b3ec5d2011-09-14 15:07:05 -070053" 'rm DEST' removes the destination file and fails if it's missing.\n"
54" 'strip DEST' strips the binary destination file.\n"
The Android Open Source Project88b60792009-03-03 19:28:42 -080055" If SRC is supplied, the file names can be different.\n"
56" SRCPATTERN is a pattern for the filenames.\n"
57"\n";
58
59int usage()
60{
61 fwrite(USAGE, strlen(USAGE), 1, stderr);
62 return 1;
63}
64
65static bool
66add_variable(const char* arg) {
67 const char* p = arg;
68 while (*p && *p != '=') p++;
69
70 if (*p == 0 || p == arg || p[1] == 0) {
71 return false;
72 }
73
74 ostringstream var;
75 var << "${" << string(arg, p-arg) << "}";
76 g_variables[var.str()] = string(p+1);
77 return true;
78}
79
Raphael0b3ec5d2011-09-14 15:07:05 -070080static void
81debug_printf(const char* format, ...)
82{
83 if (g_debug) {
84 fflush(stderr);
85 va_list ap;
86 va_start(ap, format);
87 vprintf(format, ap);
88 va_end(ap);
89 fflush(stdout);
90 }
91}
92
Deepanshu Gupta9c5564e2014-07-15 17:22:00 -070093// Escape the filename so that it can be added to the makefile properly.
94static string
95escape_filename(const string name)
96{
97 ostringstream new_name;
98 for (string::const_iterator iter = name.begin(); iter != name.end(); ++iter)
99 {
100 switch (*iter)
101 {
102 case '$':
103 new_name << "$$";
104 break;
105 default:
106 new_name << *iter;
107 break;
108 }
109 }
110 return new_name.str();
111}
112
The Android Open Source Project88b60792009-03-03 19:28:42 -0800113int
114main(int argc, char* const* argv)
115{
116 int err;
117 bool done = false;
118 while (!done) {
Raphael0b3ec5d2011-09-14 15:07:05 -0700119 int opt = getopt(argc, argv, "f:I:o:hlm:v:d");
The Android Open Source Project88b60792009-03-03 19:28:42 -0800120 switch (opt)
121 {
122 case -1:
123 done = true;
124 break;
125 case 'f':
126 g_listFiles.push_back(string(optarg));
127 break;
128 case 'I':
129 g_inputBases.push_back(string(optarg));
130 break;
131 case 'o':
132 if (g_outputBase.length() != 0) {
133 fprintf(stderr, "%s: -o may only be supplied once -- "
134 "-o %s\n", argv[0], optarg);
135 return usage();
136 }
137 g_outputBase = optarg;
138 break;
139 case 'l':
140 g_useHardLinks = true;
141 break;
142 case 'm':
143 if (g_dependency.length() != 0) {
144 fprintf(stderr, "%s: -m may only be supplied once -- "
145 "-m %s\n", argv[0], optarg);
146 return usage();
147 }
148 g_dependency = optarg;
149 break;
150 case 'v':
151 if (!add_variable(optarg)) {
152 fprintf(stderr, "%s Invalid expression in '-v %s': "
153 "expected format is '-v VAR=VALUE'.\n",
154 argv[0], optarg);
155 return usage();
156 }
157 break;
Raphael0b3ec5d2011-09-14 15:07:05 -0700158 case 'd':
159 g_debug = true;
160 break;
The Android Open Source Project88b60792009-03-03 19:28:42 -0800161 default:
162 case '?':
163 case 'h':
164 return usage();
165 }
166 }
167 if (optind != argc) {
168 fprintf(stderr, "%s: invalid argument -- %s\n", argv[0], argv[optind]);
169 return usage();
170 }
171
172 if (g_listFiles.size() == 0) {
173 fprintf(stderr, "%s: At least one -f option must be supplied.\n",
174 argv[0]);
175 return usage();
176 }
177
178 if (g_inputBases.size() == 0) {
179 fprintf(stderr, "%s: At least one -I option must be supplied.\n",
180 argv[0]);
181 return usage();
182 }
183
184 if (g_outputBase.length() == 0) {
185 fprintf(stderr, "%s: -o option must be supplied.\n", argv[0]);
186 return usage();
187 }
188
189
190#if 0
191 for (vector<string>::iterator it=g_listFiles.begin();
192 it!=g_listFiles.end(); it++) {
193 printf("-f \"%s\"\n", it->c_str());
194 }
195 for (vector<string>::iterator it=g_inputBases.begin();
196 it!=g_inputBases.end(); it++) {
197 printf("-I \"%s\"\n", it->c_str());
198 }
199 printf("-o \"%s\"\n", g_outputBase.c_str());
200 if (g_useHardLinks) {
201 printf("-l\n");
202 }
203#endif
204
205 vector<FileRecord> files;
206 vector<FileRecord> more;
207 vector<string> excludes;
208 set<string> directories;
209 set<string> deleted;
210
211 // read file lists
212 for (vector<string>::iterator it=g_listFiles.begin();
Raphael0b3ec5d2011-09-14 15:07:05 -0700213 it!=g_listFiles.end(); it++) {
The Android Open Source Project88b60792009-03-03 19:28:42 -0800214 err = read_list_file(*it, g_variables, &files, &excludes);
215 if (err != 0) {
216 return err;
217 }
218 }
219
220 // look for input files
221 err = 0;
222 for (vector<FileRecord>::iterator it=files.begin();
223 it!=files.end(); it++) {
224 err |= locate(&(*it), g_inputBases);
The Android Open Source Project88b60792009-03-03 19:28:42 -0800225 }
Raphael0b3ec5d2011-09-14 15:07:05 -0700226
The Android Open Source Project88b60792009-03-03 19:28:42 -0800227 // expand the directories that we should copy into a list of files
228 for (vector<FileRecord>::iterator it=files.begin();
229 it!=files.end(); it++) {
230 if (it->sourceIsDir) {
231 err |= list_dir(*it, excludes, &more);
232 }
233 }
234 for (vector<FileRecord>::iterator it=more.begin();
235 it!=more.end(); it++) {
236 files.push_back(*it);
237 }
238
239 // get the name and modtime of the output files
240 for (vector<FileRecord>::iterator it=files.begin();
241 it!=files.end(); it++) {
242 stat_out(g_outputBase, &(*it));
243 }
244
245 if (err != 0) {
246 return 1;
247 }
248
249 // gather directories
250 for (vector<FileRecord>::iterator it=files.begin();
251 it!=files.end(); it++) {
252 if (it->sourceIsDir) {
253 directories.insert(it->outPath);
254 } else {
255 string s = dir_part(it->outPath);
256 if (s != ".") {
257 directories.insert(s);
258 }
259 }
260 }
261
Raphael0b3ec5d2011-09-14 15:07:05 -0700262 // gather files that should become directores
263 // and directories that should become files
The Android Open Source Project88b60792009-03-03 19:28:42 -0800264 for (vector<FileRecord>::iterator it=files.begin();
265 it!=files.end(); it++) {
266 if (it->outMod != 0 && it->sourceIsDir != it->outIsDir) {
267 deleted.insert(it->outPath);
268 }
269 }
270
271 // delete files
272 for (set<string>::iterator it=deleted.begin();
273 it!=deleted.end(); it++) {
Raphael0b3ec5d2011-09-14 15:07:05 -0700274 debug_printf("deleting %s\n", it->c_str());
The Android Open Source Project88b60792009-03-03 19:28:42 -0800275 err = remove_recursively(*it);
276 if (err != 0) {
277 return err;
278 }
279 }
280
Raphael0b3ec5d2011-09-14 15:07:05 -0700281 // remove all files or directories as requested from the input atree file.
282 // must be done before create new directories.
283 for (vector<FileRecord>::iterator it=files.begin();
284 it!=files.end(); it++) {
285 if (!it->sourceIsDir) {
286 if (it->fileOp == FILE_OP_REMOVE &&
287 deleted.count(it->outPath) == 0) {
288 debug_printf("remove %s\n", it->outPath.c_str());
289 err = remove_recursively(it->outPath);
290 if (err != 0) {
291 return err;
292 }
293 }
294 }
295 }
296
The Android Open Source Project88b60792009-03-03 19:28:42 -0800297 // make directories
298 for (set<string>::iterator it=directories.begin();
299 it!=directories.end(); it++) {
Raphael0b3ec5d2011-09-14 15:07:05 -0700300 debug_printf("mkdir %s\n", it->c_str());
The Android Open Source Project88b60792009-03-03 19:28:42 -0800301 err = mkdir_recursively(*it);
302 if (err != 0) {
303 return err;
304 }
305 }
306
Raphael0b3ec5d2011-09-14 15:07:05 -0700307 // copy (or link) files that are newer or of different size
The Android Open Source Project88b60792009-03-03 19:28:42 -0800308 for (vector<FileRecord>::iterator it=files.begin();
309 it!=files.end(); it++) {
310 if (!it->sourceIsDir) {
Raphael0b3ec5d2011-09-14 15:07:05 -0700311 if (it->fileOp == FILE_OP_REMOVE) {
312 continue;
The Android Open Source Project88b60792009-03-03 19:28:42 -0800313 }
314
Raphael0b3ec5d2011-09-14 15:07:05 -0700315 debug_printf("copy %s(%ld) ==> %s(%ld)",
316 it->sourcePath.c_str(), it->sourceMod,
317 it->outPath.c_str(), it->outMod);
318
319 if (it->outSize != it->sourceSize || it->outMod < it->sourceMod) {
The Android Open Source Project88b60792009-03-03 19:28:42 -0800320 err = copy_file(it->sourcePath, it->outPath);
Raphael0b3ec5d2011-09-14 15:07:05 -0700321 debug_printf(" done.\n");
The Android Open Source Project88b60792009-03-03 19:28:42 -0800322 if (err != 0) {
323 return err;
324 }
325 } else {
Raphael0b3ec5d2011-09-14 15:07:05 -0700326 debug_printf(" skipping.\n");
327 }
328
329 if (it->fileOp == FILE_OP_STRIP) {
330 debug_printf("strip %s\n", it->outPath.c_str());
331 err = strip_file(it->outPath);
332 if (err != 0) {
333 return err;
The Android Open Source Project88b60792009-03-03 19:28:42 -0800334 }
335 }
336 }
337 }
338
339 // output the dependency file
340 if (g_dependency.length() != 0) {
341 FILE *f = fopen(g_dependency.c_str(), "w");
342 if (f != NULL) {
343 fprintf(f, "ATREE_FILES := $(ATREE_FILES) \\\n");
344 for (vector<FileRecord>::iterator it=files.begin();
345 it!=files.end(); it++) {
346 if (!it->sourceIsDir) {
Deepanshu Gupta9c5564e2014-07-15 17:22:00 -0700347 fprintf(f, "%s \\\n",
348 escape_filename(it->sourcePath).c_str());
The Android Open Source Project88b60792009-03-03 19:28:42 -0800349 }
350 }
351 fprintf(f, "\n");
352 fclose(f);
353 } else {
354 fprintf(stderr, "error opening manifest file for write: %s\n",
355 g_dependency.c_str());
356 }
357 }
358
359 return 0;
360}