blob: 2ba284fdecbcb071329b1cb77b96b4be0e4c0dd2 [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
The Android Open Source Project88b60792009-03-03 19:28:42 -080093int
94main(int argc, char* const* argv)
95{
96 int err;
97 bool done = false;
98 while (!done) {
Raphael0b3ec5d2011-09-14 15:07:05 -070099 int opt = getopt(argc, argv, "f:I:o:hlm:v:d");
The Android Open Source Project88b60792009-03-03 19:28:42 -0800100 switch (opt)
101 {
102 case -1:
103 done = true;
104 break;
105 case 'f':
106 g_listFiles.push_back(string(optarg));
107 break;
108 case 'I':
109 g_inputBases.push_back(string(optarg));
110 break;
111 case 'o':
112 if (g_outputBase.length() != 0) {
113 fprintf(stderr, "%s: -o may only be supplied once -- "
114 "-o %s\n", argv[0], optarg);
115 return usage();
116 }
117 g_outputBase = optarg;
118 break;
119 case 'l':
120 g_useHardLinks = true;
121 break;
122 case 'm':
123 if (g_dependency.length() != 0) {
124 fprintf(stderr, "%s: -m may only be supplied once -- "
125 "-m %s\n", argv[0], optarg);
126 return usage();
127 }
128 g_dependency = optarg;
129 break;
130 case 'v':
131 if (!add_variable(optarg)) {
132 fprintf(stderr, "%s Invalid expression in '-v %s': "
133 "expected format is '-v VAR=VALUE'.\n",
134 argv[0], optarg);
135 return usage();
136 }
137 break;
Raphael0b3ec5d2011-09-14 15:07:05 -0700138 case 'd':
139 g_debug = true;
140 break;
The Android Open Source Project88b60792009-03-03 19:28:42 -0800141 default:
142 case '?':
143 case 'h':
144 return usage();
145 }
146 }
147 if (optind != argc) {
148 fprintf(stderr, "%s: invalid argument -- %s\n", argv[0], argv[optind]);
149 return usage();
150 }
151
152 if (g_listFiles.size() == 0) {
153 fprintf(stderr, "%s: At least one -f option must be supplied.\n",
154 argv[0]);
155 return usage();
156 }
157
158 if (g_inputBases.size() == 0) {
159 fprintf(stderr, "%s: At least one -I option must be supplied.\n",
160 argv[0]);
161 return usage();
162 }
163
164 if (g_outputBase.length() == 0) {
165 fprintf(stderr, "%s: -o option must be supplied.\n", argv[0]);
166 return usage();
167 }
168
169
170#if 0
171 for (vector<string>::iterator it=g_listFiles.begin();
172 it!=g_listFiles.end(); it++) {
173 printf("-f \"%s\"\n", it->c_str());
174 }
175 for (vector<string>::iterator it=g_inputBases.begin();
176 it!=g_inputBases.end(); it++) {
177 printf("-I \"%s\"\n", it->c_str());
178 }
179 printf("-o \"%s\"\n", g_outputBase.c_str());
180 if (g_useHardLinks) {
181 printf("-l\n");
182 }
183#endif
184
185 vector<FileRecord> files;
186 vector<FileRecord> more;
187 vector<string> excludes;
188 set<string> directories;
189 set<string> deleted;
190
191 // read file lists
192 for (vector<string>::iterator it=g_listFiles.begin();
Raphael0b3ec5d2011-09-14 15:07:05 -0700193 it!=g_listFiles.end(); it++) {
The Android Open Source Project88b60792009-03-03 19:28:42 -0800194 err = read_list_file(*it, g_variables, &files, &excludes);
195 if (err != 0) {
196 return err;
197 }
198 }
199
200 // look for input files
201 err = 0;
202 for (vector<FileRecord>::iterator it=files.begin();
203 it!=files.end(); it++) {
204 err |= locate(&(*it), g_inputBases);
The Android Open Source Project88b60792009-03-03 19:28:42 -0800205 }
Raphael0b3ec5d2011-09-14 15:07:05 -0700206
The Android Open Source Project88b60792009-03-03 19:28:42 -0800207 // expand the directories that we should copy into a list of files
208 for (vector<FileRecord>::iterator it=files.begin();
209 it!=files.end(); it++) {
210 if (it->sourceIsDir) {
211 err |= list_dir(*it, excludes, &more);
212 }
213 }
214 for (vector<FileRecord>::iterator it=more.begin();
215 it!=more.end(); it++) {
216 files.push_back(*it);
217 }
218
219 // get the name and modtime of the output files
220 for (vector<FileRecord>::iterator it=files.begin();
221 it!=files.end(); it++) {
222 stat_out(g_outputBase, &(*it));
223 }
224
225 if (err != 0) {
226 return 1;
227 }
228
229 // gather directories
230 for (vector<FileRecord>::iterator it=files.begin();
231 it!=files.end(); it++) {
232 if (it->sourceIsDir) {
233 directories.insert(it->outPath);
234 } else {
235 string s = dir_part(it->outPath);
236 if (s != ".") {
237 directories.insert(s);
238 }
239 }
240 }
241
Raphael0b3ec5d2011-09-14 15:07:05 -0700242 // gather files that should become directores
243 // and directories that should become files
The Android Open Source Project88b60792009-03-03 19:28:42 -0800244 for (vector<FileRecord>::iterator it=files.begin();
245 it!=files.end(); it++) {
246 if (it->outMod != 0 && it->sourceIsDir != it->outIsDir) {
247 deleted.insert(it->outPath);
248 }
249 }
250
251 // delete files
252 for (set<string>::iterator it=deleted.begin();
253 it!=deleted.end(); it++) {
Raphael0b3ec5d2011-09-14 15:07:05 -0700254 debug_printf("deleting %s\n", it->c_str());
The Android Open Source Project88b60792009-03-03 19:28:42 -0800255 err = remove_recursively(*it);
256 if (err != 0) {
257 return err;
258 }
259 }
260
Raphael0b3ec5d2011-09-14 15:07:05 -0700261 // remove all files or directories as requested from the input atree file.
262 // must be done before create new directories.
263 for (vector<FileRecord>::iterator it=files.begin();
264 it!=files.end(); it++) {
265 if (!it->sourceIsDir) {
266 if (it->fileOp == FILE_OP_REMOVE &&
267 deleted.count(it->outPath) == 0) {
268 debug_printf("remove %s\n", it->outPath.c_str());
269 err = remove_recursively(it->outPath);
270 if (err != 0) {
271 return err;
272 }
273 }
274 }
275 }
276
The Android Open Source Project88b60792009-03-03 19:28:42 -0800277 // make directories
278 for (set<string>::iterator it=directories.begin();
279 it!=directories.end(); it++) {
Raphael0b3ec5d2011-09-14 15:07:05 -0700280 debug_printf("mkdir %s\n", it->c_str());
The Android Open Source Project88b60792009-03-03 19:28:42 -0800281 err = mkdir_recursively(*it);
282 if (err != 0) {
283 return err;
284 }
285 }
286
Raphael0b3ec5d2011-09-14 15:07:05 -0700287 // copy (or link) files that are newer or of different size
The Android Open Source Project88b60792009-03-03 19:28:42 -0800288 for (vector<FileRecord>::iterator it=files.begin();
289 it!=files.end(); it++) {
290 if (!it->sourceIsDir) {
Raphael0b3ec5d2011-09-14 15:07:05 -0700291 if (it->fileOp == FILE_OP_REMOVE) {
292 continue;
The Android Open Source Project88b60792009-03-03 19:28:42 -0800293 }
294
Raphael0b3ec5d2011-09-14 15:07:05 -0700295 debug_printf("copy %s(%ld) ==> %s(%ld)",
296 it->sourcePath.c_str(), it->sourceMod,
297 it->outPath.c_str(), it->outMod);
298
299 if (it->outSize != it->sourceSize || it->outMod < it->sourceMod) {
The Android Open Source Project88b60792009-03-03 19:28:42 -0800300 err = copy_file(it->sourcePath, it->outPath);
Raphael0b3ec5d2011-09-14 15:07:05 -0700301 debug_printf(" done.\n");
The Android Open Source Project88b60792009-03-03 19:28:42 -0800302 if (err != 0) {
303 return err;
304 }
305 } else {
Raphael0b3ec5d2011-09-14 15:07:05 -0700306 debug_printf(" skipping.\n");
307 }
308
309 if (it->fileOp == FILE_OP_STRIP) {
310 debug_printf("strip %s\n", it->outPath.c_str());
311 err = strip_file(it->outPath);
312 if (err != 0) {
313 return err;
The Android Open Source Project88b60792009-03-03 19:28:42 -0800314 }
315 }
316 }
317 }
318
319 // output the dependency file
320 if (g_dependency.length() != 0) {
321 FILE *f = fopen(g_dependency.c_str(), "w");
322 if (f != NULL) {
323 fprintf(f, "ATREE_FILES := $(ATREE_FILES) \\\n");
324 for (vector<FileRecord>::iterator it=files.begin();
325 it!=files.end(); it++) {
326 if (!it->sourceIsDir) {
327 fprintf(f, "%s \\\n", it->sourcePath.c_str());
328 }
329 }
330 fprintf(f, "\n");
331 fclose(f);
332 } else {
333 fprintf(stderr, "error opening manifest file for write: %s\n",
334 g_dependency.c_str());
335 }
336 }
337
338 return 0;
339}