Add rm and strip abilities to atree.

The new line syntax is:
  [SRC] [rm|strip] DEST

This allows one to write things like this in atree:

  bin/src
  bin/src bin/dest
  bin/src "bin/another file name"

  rm dest/file
  rm dest/dir  # recursive

  strip bin/src
  bin/src strip bin/dest

Src and dest can contain spaces if full enclosed in double-quotes.
The strip command can be overridden using the STRIP env var.

Change-Id: I22aae7a87c36c082e1aab87132099a3c644914da
This commit is contained in:
Raphael
2011-09-14 15:07:05 -07:00
parent 4d7ddab160
commit 0b3ec5d32f
5 changed files with 191 additions and 62 deletions

View File

@@ -1,6 +1,8 @@
#include <stdio.h> #include <stdio.h>
#include <stdlib.h>
#include <string.h> #include <string.h>
#include <unistd.h> #include <unistd.h>
#include <stdarg.h>
#include "options.h" #include "options.h"
#include "files.h" #include "files.h"
#include "fs.h" #include "fs.h"
@@ -10,7 +12,7 @@
using namespace std; using namespace std;
bool g_debug = false; bool g_debug = getenv("ATREE_DEBUG") != NULL;
vector<string> g_listFiles; vector<string> g_listFiles;
vector<string> g_inputBases; vector<string> g_inputBases;
map<string, string> g_variables; map<string, string> g_variables;
@@ -33,6 +35,7 @@ const char* USAGE =
" -m DEPENDENCY Output a make-formatted file containing the list.\n" " -m DEPENDENCY Output a make-formatted file containing the list.\n"
" of files included. It sets the variable ATREE_FILES.\n" " of files included. It sets the variable ATREE_FILES.\n"
" -v VAR=VAL Replaces ${VAR} by VAL when reading input files.\n" " -v VAR=VAL Replaces ${VAR} by VAL when reading input files.\n"
" -d Verbose debug mode.\n"
"\n" "\n"
"FILELIST file format:\n" "FILELIST file format:\n"
" The FILELIST files contain the list of files that will end up\n" " The FILELIST files contain the list of files that will end up\n"
@@ -42,11 +45,13 @@ const char* USAGE =
" In a FILELIST file, comment lines start with a #. Other lines\n" " In a FILELIST file, comment lines start with a #. Other lines\n"
" are of the format:\n" " are of the format:\n"
"\n" "\n"
" DEST\n" " [rm|strip] DEST\n"
" SRC DEST\n" " SRC [strip] DEST\n"
" -SRCPATTERN\n" " -SRCPATTERN\n"
"\n" "\n"
" DEST should be path relative to the output directory.\n" " DEST should be path relative to the output directory.\n"
" 'rm DEST' removes the destination file and fails if it's missing.\n"
" 'strip DEST' strips the binary destination file.\n"
" If SRC is supplied, the file names can be different.\n" " If SRC is supplied, the file names can be different.\n"
" SRCPATTERN is a pattern for the filenames.\n" " SRCPATTERN is a pattern for the filenames.\n"
"\n"; "\n";
@@ -72,13 +77,26 @@ add_variable(const char* arg) {
return true; return true;
} }
static void
debug_printf(const char* format, ...)
{
if (g_debug) {
fflush(stderr);
va_list ap;
va_start(ap, format);
vprintf(format, ap);
va_end(ap);
fflush(stdout);
}
}
int int
main(int argc, char* const* argv) main(int argc, char* const* argv)
{ {
int err; int err;
bool done = false; bool done = false;
while (!done) { while (!done) {
int opt = getopt(argc, argv, "f:I:o:hlm:v:"); int opt = getopt(argc, argv, "f:I:o:hlm:v:d");
switch (opt) switch (opt)
{ {
case -1: case -1:
@@ -117,6 +135,9 @@ main(int argc, char* const* argv)
return usage(); return usage();
} }
break; break;
case 'd':
g_debug = true;
break;
default: default:
case '?': case '?':
case 'h': case 'h':
@@ -169,7 +190,7 @@ main(int argc, char* const* argv)
// read file lists // read file lists
for (vector<string>::iterator it=g_listFiles.begin(); for (vector<string>::iterator it=g_listFiles.begin();
it!=g_listFiles.end(); it++) { it!=g_listFiles.end(); it++) {
err = read_list_file(*it, g_variables, &files, &excludes); err = read_list_file(*it, g_variables, &files, &excludes);
if (err != 0) { if (err != 0) {
return err; return err;
@@ -181,9 +202,8 @@ main(int argc, char* const* argv)
for (vector<FileRecord>::iterator it=files.begin(); for (vector<FileRecord>::iterator it=files.begin();
it!=files.end(); it++) { it!=files.end(); it++) {
err |= locate(&(*it), g_inputBases); err |= locate(&(*it), g_inputBases);
} }
// expand the directories that we should copy into a list of files // expand the directories that we should copy into a list of files
for (vector<FileRecord>::iterator it=files.begin(); for (vector<FileRecord>::iterator it=files.begin();
it!=files.end(); it++) { it!=files.end(); it++) {
@@ -219,8 +239,8 @@ main(int argc, char* const* argv)
} }
} }
// gather files that should become directores and directories that should // gather files that should become directores
// become files // and directories that should become files
for (vector<FileRecord>::iterator it=files.begin(); for (vector<FileRecord>::iterator it=files.begin();
it!=files.end(); it++) { it!=files.end(); it++) {
if (it->outMod != 0 && it->sourceIsDir != it->outIsDir) { if (it->outMod != 0 && it->sourceIsDir != it->outIsDir) {
@@ -231,48 +251,66 @@ main(int argc, char* const* argv)
// delete files // delete files
for (set<string>::iterator it=deleted.begin(); for (set<string>::iterator it=deleted.begin();
it!=deleted.end(); it++) { it!=deleted.end(); it++) {
if (g_debug) { debug_printf("deleting %s\n", it->c_str());
printf("deleting %s\n", it->c_str());
}
err = remove_recursively(*it); err = remove_recursively(*it);
if (err != 0) { if (err != 0) {
return err; return err;
} }
} }
// remove all files or directories as requested from the input atree file.
// must be done before create new directories.
for (vector<FileRecord>::iterator it=files.begin();
it!=files.end(); it++) {
if (!it->sourceIsDir) {
if (it->fileOp == FILE_OP_REMOVE &&
deleted.count(it->outPath) == 0) {
debug_printf("remove %s\n", it->outPath.c_str());
err = remove_recursively(it->outPath);
if (err != 0) {
return err;
}
}
}
}
// make directories // make directories
for (set<string>::iterator it=directories.begin(); for (set<string>::iterator it=directories.begin();
it!=directories.end(); it++) { it!=directories.end(); it++) {
if (g_debug) { debug_printf("mkdir %s\n", it->c_str());
printf("mkdir %s\n", it->c_str());
}
err = mkdir_recursively(*it); err = mkdir_recursively(*it);
if (err != 0) { if (err != 0) {
return err; return err;
} }
} }
// copy (or link) files // copy (or link) files that are newer or of different size
for (vector<FileRecord>::iterator it=files.begin(); for (vector<FileRecord>::iterator it=files.begin();
it!=files.end(); it++) { it!=files.end(); it++) {
if (!it->sourceIsDir) { if (!it->sourceIsDir) {
if (g_debug) { if (it->fileOp == FILE_OP_REMOVE) {
printf("copy %s(%ld) ==> %s(%ld)", it->sourcePath.c_str(), continue;
it->sourceMod, it->outPath.c_str(), it->outMod);
fflush(stdout);
} }
if (it->outMod < it->sourceMod) { debug_printf("copy %s(%ld) ==> %s(%ld)",
it->sourcePath.c_str(), it->sourceMod,
it->outPath.c_str(), it->outMod);
if (it->outSize != it->sourceSize || it->outMod < it->sourceMod) {
err = copy_file(it->sourcePath, it->outPath); err = copy_file(it->sourcePath, it->outPath);
if (g_debug) { debug_printf(" done.\n");
printf(" done.\n");
}
if (err != 0) { if (err != 0) {
return err; return err;
} }
} else { } else {
if (g_debug) { debug_printf(" skipping.\n");
printf(" skipping.\n"); }
if (it->fileOp == FILE_OP_STRIP) {
debug_printf("strip %s\n", it->outPath.c_str());
err = strip_file(it->outPath);
if (err != 0) {
return err;
} }
} }
} }

View File

@@ -62,7 +62,7 @@ void
split_line(const char* p, vector<string>* out) split_line(const char* p, vector<string>* out)
{ {
const char* q = p; const char* q = p;
enum { WHITE, TEXT } state = WHITE; enum { WHITE, TEXT, IN_QUOTE } state = WHITE;
while (*p) { while (*p) {
if (*p == '#') { if (*p == '#') {
break; break;
@@ -73,13 +73,25 @@ split_line(const char* p, vector<string>* out)
case WHITE: case WHITE:
if (!isspace(*p)) { if (!isspace(*p)) {
q = p; q = p;
state = TEXT; state = (*p == '"') ? IN_QUOTE : TEXT;
} }
break; break;
case IN_QUOTE:
if (*p == '"') {
state = TEXT;
break;
}
// otherwise fall-through to TEXT case
case TEXT: case TEXT:
if (isspace(*p)) { if (state != IN_QUOTE && isspace(*p)) {
if (q != p) { if (q != p) {
out->push_back(string(q, p-q)); const char* start = q;
size_t len = p-q;
if (len > 2 && *start == '"' && start[len - 1] == '"') {
start++;
len -= 2;
}
out->push_back(string(start, len));
} }
state = WHITE; state = WHITE;
} }
@@ -88,17 +100,25 @@ split_line(const char* p, vector<string>* out)
p++; p++;
} }
if (state == TEXT) { if (state == TEXT) {
out->push_back(string(q, p-q)); const char* start = q;
size_t len = p-q;
if (len > 2 && *start == '"' && start[len - 1] == '"') {
start++;
len -= 2;
}
out->push_back(string(start, len));
} }
} }
static void static void
add_file(vector<FileRecord>* files, const string& listFile, int listLine, add_file(vector<FileRecord>* files, const FileOpType fileOp,
const string& listFile, int listLine,
const string& sourceName, const string& outName) const string& sourceName, const string& outName)
{ {
FileRecord rec; FileRecord rec;
rec.listFile = listFile; rec.listFile = listFile;
rec.listLine = listLine; rec.listLine = listLine;
rec.fileOp = fileOp;
rec.sourceName = sourceName; rec.sourceName = sourceName;
rec.outName = outName; rec.outName = outName;
files->push_back(rec); files->push_back(rec);
@@ -182,7 +202,7 @@ read_list_file(const string& filename,
err = errno; err = errno;
goto cleanup; goto cleanup;
} }
size = ftell(f); size = ftell(f);
err = fseek(f, 0, SEEK_SET); err = fseek(f, 0, SEEK_SET);
@@ -245,35 +265,52 @@ read_list_file(const string& filename,
} }
printf("]\n"); printf("]\n");
#endif #endif
FileOpType op = FILE_OP_COPY;
if (words.size() == 1) { string paths[2];
// pattern: DEST int pcount = 0;
bool error = false; string errstr;
string w0 = replace_variables(words[0], variables, &error); for (vector<string>::iterator it = words.begin(); it != words.end(); ++it) {
if (error) { const string& word = *it;
err = 1; if (word == "rm") {
goto cleanup; if (op != FILE_OP_COPY) {
errstr = "Error: you can only specifiy 'rm' or 'strip' once per line.";
break;
}
op = FILE_OP_REMOVE;
} else if (word == "strip") {
if (op != FILE_OP_COPY) {
errstr = "Error: you can only specifiy 'rm' or 'strip' once per line.";
break;
}
op = FILE_OP_STRIP;
} else if (pcount < 2) {
bool error = false;
paths[pcount++] = replace_variables(word, variables, &error);
if (error) {
err = 1;
goto cleanup;
}
} else {
errstr = "Error: More than 2 paths per line.";
break;
} }
add_file(files, filename, i+1, w0, w0);
} }
else if (words.size() == 2) {
// pattern: SRC DEST if (pcount == 0 && !errstr.empty()) {
bool error = false; errstr = "Error: No path found on line.";
string w0, w1;
w0 = replace_variables(words[0], variables, &error);
if (!error) {
w1 = replace_variables(words[1], variables, &error);
}
if (error) {
err = 1;
goto cleanup;
}
add_file(files, filename, i+1, w0, w1);
} }
else {
fprintf(stderr, "%s:%d: bad format: %s\n", filename.c_str(), if (!errstr.empty()) {
i+1, p); fprintf(stderr, "%s:%d: bad format: %s\n%s\nExpected: [SRC] [rm|strip] DEST\n",
filename.c_str(), i+1, p, errstr.c_str());
err = 1; err = 1;
} else {
if (pcount == 1) {
// pattern: [rm|strip] DEST
paths[1] = paths[0];
}
add_file(files, op, filename, i+1, paths[0], paths[1]);
} }
} }
p = q; p = q;
@@ -293,6 +330,14 @@ cleanup:
int int
locate(FileRecord* rec, const vector<string>& search) locate(FileRecord* rec, const vector<string>& search)
{ {
if (rec->fileOp == FILE_OP_REMOVE) {
// Don't touch source files when removing a destination.
rec->sourceMod = 0;
rec->sourceSize = 0;
rec->sourceIsDir = false;
return 0;
}
int err; int err;
for (vector<string>::const_iterator it=search.begin(); for (vector<string>::const_iterator it=search.begin();
@@ -304,6 +349,7 @@ locate(FileRecord* rec, const vector<string>& search)
rec->sourceBase = *it; rec->sourceBase = *it;
rec->sourcePath = full; rec->sourcePath = full;
rec->sourceMod = st.st_mtime; rec->sourceMod = st.st_mtime;
rec->sourceSize = st.st_size;
rec->sourceIsDir = S_ISDIR(st.st_mode); rec->sourceIsDir = S_ISDIR(st.st_mode);
return 0; return 0;
} }
@@ -324,9 +370,11 @@ stat_out(const string& base, FileRecord* rec)
err = stat(rec->outPath.c_str(), &st); err = stat(rec->outPath.c_str(), &st);
if (err == 0) { if (err == 0) {
rec->outMod = st.st_mtime; rec->outMod = st.st_mtime;
rec->outSize = st.st_size;
rec->outIsDir = S_ISDIR(st.st_mode); rec->outIsDir = S_ISDIR(st.st_mode);
} else { } else {
rec->outMod = 0; rec->outMod = 0;
rec->outSize = 0;
rec->outIsDir = false; rec->outIsDir = false;
} }
} }
@@ -427,3 +475,8 @@ list_dir(const FileRecord& rec, const vector<string>& excludes,
{ {
return list_dir("", rec, excludes, files); return list_dir("", rec, excludes, files);
} }
FileRecord::FileRecord() {
fileOp = FILE_OP_COPY;
}

View File

@@ -8,8 +8,16 @@
using namespace std; using namespace std;
enum FileOpType {
FILE_OP_COPY = 0,
FILE_OP_REMOVE,
FILE_OP_STRIP
};
struct FileRecord struct FileRecord
{ {
FileRecord();
string listFile; string listFile;
int listLine; int listLine;
@@ -18,9 +26,12 @@ struct FileRecord
string sourcePath; string sourcePath;
bool sourceIsDir; bool sourceIsDir;
time_t sourceMod; time_t sourceMod;
off_t sourceSize;
FileOpType fileOp;
string outName; string outName;
string outPath; string outPath;
off_t outSize;
time_t outMod; time_t outMod;
bool outIsDir; bool outIsDir;
unsigned int mode; unsigned int mode;

View File

@@ -1,7 +1,9 @@
#include "fs.h" #include "fs.h"
#include "files.h" #include "files.h"
#include <unistd.h> #include <unistd.h>
#include <stdlib.h>
#include <sys/types.h> #include <sys/types.h>
#include <sys/wait.h>
#include <dirent.h> #include <dirent.h>
#include <string> #include <string>
#include <vector> #include <vector>
@@ -64,10 +66,10 @@ remove_recursively(const string& path)
#ifdef HAVE_DIRENT_D_TYPE #ifdef HAVE_DIRENT_D_TYPE
bool is_directory = (ent->d_type == DT_DIR); bool is_directory = (ent->d_type == DT_DIR);
#else #else
// If dirent.d_type is missing, then use stat instead // If dirent.d_type is missing, then use stat instead
struct stat stat_buf; struct stat stat_buf;
stat(full.c_str(), &stat_buf); stat(full.c_str(), &stat_buf);
bool is_directory = S_ISDIR(stat_buf.st_mode); bool is_directory = S_ISDIR(stat_buf.st_mode);
#endif #endif
if (is_directory) { if (is_directory) {
dirs.push_back(full); dirs.push_back(full);
@@ -146,3 +148,27 @@ copy_file(const string& src, const string& dst)
COPY_NO_DEREFERENCE | COPY_FORCE | COPY_PERMISSIONS); COPY_NO_DEREFERENCE | COPY_FORCE | COPY_PERMISSIONS);
return err; return err;
} }
int
strip_file(const string& path)
{
// Default strip command to run is "strip" unless overridden by the STRIP env var.
const char* strip_cmd = getenv("STRIP");
if (!strip_cmd || !strip_cmd[0]) {
strip_cmd = "strip";
}
pid_t pid = fork();
if (pid == -1) {
// Fork failed. errno should be set.
return -1;
} else if (pid == 0) {
// Exec in the child. Only returns if execve failed.
return execlp(strip_cmd, strip_cmd, path.c_str(), (char *)NULL);
} else {
// Wait for child pid and return its exit code.
int status;
waitpid(pid, &status, 0);
return status;
}
}

View File

@@ -8,5 +8,6 @@ using namespace std;
int remove_recursively(const string& path); int remove_recursively(const string& path);
int mkdir_recursively(const string& path); int mkdir_recursively(const string& path);
int copy_file(const string& src, const string& dst); int copy_file(const string& src, const string& dst);
int strip_file(const string& path);
#endif // FS_H #endif // FS_H