blob: aee2b3ce2e9cdf8d65364bc4d540565bd76896eb [file] [log] [blame]
The Android Open Source Project88b60792009-03-03 19:28:42 -08001#include <stdio.h>
2#include <string.h>
3#include <unistd.h>
4#include "options.h"
5#include "files.h"
6#include "fs.h"
7#include <set>
8#include <iostream>
9#include <sstream>
10
11using namespace std;
12
13bool g_debug = false;
14vector<string> g_listFiles;
15vector<string> g_inputBases;
16map<string, string> g_variables;
17string g_outputBase;
18string g_dependency;
19bool g_useHardLinks = false;
20
21const char* USAGE =
22"\n"
23"Usage: atree OPTIONS\n"
24"\n"
25"Options:\n"
26" -f FILELIST Specify one or more files containing the\n"
27" list of files to copy.\n"
28" -I INPUTDIR Specify one or more base directories in\n"
29" which to look for the files\n"
30" -o OUTPUTDIR Specify the directory to copy all of the\n"
31" output files to.\n"
32" -l Use hard links instead of copying the files.\n"
33" -m DEPENDENCY Output a make-formatted file containing the list.\n"
34" of files included. It sets the variable ATREE_FILES.\n"
35" -v VAR=VAL Replaces ${VAR} by VAL when reading input files.\n"
36"\n"
37"FILELIST file format:\n"
38" The FILELIST files contain the list of files that will end up\n"
39" in the final OUTPUTDIR. Atree will look for files in the INPUTDIR\n"
40" directories in the order they are specified.\n"
41"\n"
42" In a FILELIST file, comment lines start with a #. Other lines\n"
43" are of the format:\n"
44"\n"
45" DEST\n"
46" SRC DEST\n"
47" -SRCPATTERN\n"
48"\n"
49" DEST should be path relative to the output directory.\n"
50" If SRC is supplied, the file names can be different.\n"
51" SRCPATTERN is a pattern for the filenames.\n"
52"\n";
53
54int usage()
55{
56 fwrite(USAGE, strlen(USAGE), 1, stderr);
57 return 1;
58}
59
60static bool
61add_variable(const char* arg) {
62 const char* p = arg;
63 while (*p && *p != '=') p++;
64
65 if (*p == 0 || p == arg || p[1] == 0) {
66 return false;
67 }
68
69 ostringstream var;
70 var << "${" << string(arg, p-arg) << "}";
71 g_variables[var.str()] = string(p+1);
72 return true;
73}
74
75int
76main(int argc, char* const* argv)
77{
78 int err;
79 bool done = false;
80 while (!done) {
81 int opt = getopt(argc, argv, "f:I:o:hlm:v:");
82 switch (opt)
83 {
84 case -1:
85 done = true;
86 break;
87 case 'f':
88 g_listFiles.push_back(string(optarg));
89 break;
90 case 'I':
91 g_inputBases.push_back(string(optarg));
92 break;
93 case 'o':
94 if (g_outputBase.length() != 0) {
95 fprintf(stderr, "%s: -o may only be supplied once -- "
96 "-o %s\n", argv[0], optarg);
97 return usage();
98 }
99 g_outputBase = optarg;
100 break;
101 case 'l':
102 g_useHardLinks = true;
103 break;
104 case 'm':
105 if (g_dependency.length() != 0) {
106 fprintf(stderr, "%s: -m may only be supplied once -- "
107 "-m %s\n", argv[0], optarg);
108 return usage();
109 }
110 g_dependency = optarg;
111 break;
112 case 'v':
113 if (!add_variable(optarg)) {
114 fprintf(stderr, "%s Invalid expression in '-v %s': "
115 "expected format is '-v VAR=VALUE'.\n",
116 argv[0], optarg);
117 return usage();
118 }
119 break;
120 default:
121 case '?':
122 case 'h':
123 return usage();
124 }
125 }
126 if (optind != argc) {
127 fprintf(stderr, "%s: invalid argument -- %s\n", argv[0], argv[optind]);
128 return usage();
129 }
130
131 if (g_listFiles.size() == 0) {
132 fprintf(stderr, "%s: At least one -f option must be supplied.\n",
133 argv[0]);
134 return usage();
135 }
136
137 if (g_inputBases.size() == 0) {
138 fprintf(stderr, "%s: At least one -I option must be supplied.\n",
139 argv[0]);
140 return usage();
141 }
142
143 if (g_outputBase.length() == 0) {
144 fprintf(stderr, "%s: -o option must be supplied.\n", argv[0]);
145 return usage();
146 }
147
148
149#if 0
150 for (vector<string>::iterator it=g_listFiles.begin();
151 it!=g_listFiles.end(); it++) {
152 printf("-f \"%s\"\n", it->c_str());
153 }
154 for (vector<string>::iterator it=g_inputBases.begin();
155 it!=g_inputBases.end(); it++) {
156 printf("-I \"%s\"\n", it->c_str());
157 }
158 printf("-o \"%s\"\n", g_outputBase.c_str());
159 if (g_useHardLinks) {
160 printf("-l\n");
161 }
162#endif
163
164 vector<FileRecord> files;
165 vector<FileRecord> more;
166 vector<string> excludes;
167 set<string> directories;
168 set<string> deleted;
169
170 // read file lists
171 for (vector<string>::iterator it=g_listFiles.begin();
172 it!=g_listFiles.end(); it++) {
173 err = read_list_file(*it, g_variables, &files, &excludes);
174 if (err != 0) {
175 return err;
176 }
177 }
178
179 // look for input files
180 err = 0;
181 for (vector<FileRecord>::iterator it=files.begin();
182 it!=files.end(); it++) {
183 err |= locate(&(*it), g_inputBases);
184
185 }
186
187 // expand the directories that we should copy into a list of files
188 for (vector<FileRecord>::iterator it=files.begin();
189 it!=files.end(); it++) {
190 if (it->sourceIsDir) {
191 err |= list_dir(*it, excludes, &more);
192 }
193 }
194 for (vector<FileRecord>::iterator it=more.begin();
195 it!=more.end(); it++) {
196 files.push_back(*it);
197 }
198
199 // get the name and modtime of the output files
200 for (vector<FileRecord>::iterator it=files.begin();
201 it!=files.end(); it++) {
202 stat_out(g_outputBase, &(*it));
203 }
204
205 if (err != 0) {
206 return 1;
207 }
208
209 // gather directories
210 for (vector<FileRecord>::iterator it=files.begin();
211 it!=files.end(); it++) {
212 if (it->sourceIsDir) {
213 directories.insert(it->outPath);
214 } else {
215 string s = dir_part(it->outPath);
216 if (s != ".") {
217 directories.insert(s);
218 }
219 }
220 }
221
222 // gather files that should become directores and directories that should
223 // become files
224 for (vector<FileRecord>::iterator it=files.begin();
225 it!=files.end(); it++) {
226 if (it->outMod != 0 && it->sourceIsDir != it->outIsDir) {
227 deleted.insert(it->outPath);
228 }
229 }
230
231 // delete files
232 for (set<string>::iterator it=deleted.begin();
233 it!=deleted.end(); it++) {
234 if (g_debug) {
235 printf("deleting %s\n", it->c_str());
236 }
237 err = remove_recursively(*it);
238 if (err != 0) {
239 return err;
240 }
241 }
242
243 // make directories
244 for (set<string>::iterator it=directories.begin();
245 it!=directories.end(); it++) {
246 if (g_debug) {
247 printf("mkdir %s\n", it->c_str());
248 }
249 err = mkdir_recursively(*it);
250 if (err != 0) {
251 return err;
252 }
253 }
254
255 // copy (or link) files
256 for (vector<FileRecord>::iterator it=files.begin();
257 it!=files.end(); it++) {
258 if (!it->sourceIsDir) {
259 if (g_debug) {
260 printf("copy %s(%ld) ==> %s(%ld)", it->sourcePath.c_str(),
261 it->sourceMod, it->outPath.c_str(), it->outMod);
262 fflush(stdout);
263 }
264
265 if (it->outMod < it->sourceMod) {
266 err = copy_file(it->sourcePath, it->outPath);
267 if (g_debug) {
268 printf(" done.\n");
269 }
270 if (err != 0) {
271 return err;
272 }
273 } else {
274 if (g_debug) {
275 printf(" skipping.\n");
276 }
277 }
278 }
279 }
280
281 // output the dependency file
282 if (g_dependency.length() != 0) {
283 FILE *f = fopen(g_dependency.c_str(), "w");
284 if (f != NULL) {
285 fprintf(f, "ATREE_FILES := $(ATREE_FILES) \\\n");
286 for (vector<FileRecord>::iterator it=files.begin();
287 it!=files.end(); it++) {
288 if (!it->sourceIsDir) {
289 fprintf(f, "%s \\\n", it->sourcePath.c_str());
290 }
291 }
292 fprintf(f, "\n");
293 fclose(f);
294 } else {
295 fprintf(stderr, "error opening manifest file for write: %s\n",
296 g_dependency.c_str());
297 }
298 }
299
300 return 0;
301}