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