yacc -d -o parser.c parser.y
@endverbatim
+\subsection sec-special-tgt Special targets
+
+Target <tt>.PHONY</tt> marks its prerequisites as being always obsolete.
+
\subsection sec-special-var Special variables
Variable <tt>.OPTIONS</tt> is handled specially. Its content enables some
\section sec-licensing Licensing
@author Guillaume Melquiond
-@version 0.9
+@version 0.11
@date 2012-2013
@copyright
This program is free software: you can redistribute it and/or modify
- #start calls #find_rule (and #find_generic_rule) to get the rule.
- It then creates a pseudo-client if the rule has static dependencies, or
- calls #run_script otherwise. In both cases, a new job is created and its
- targets are put into #job_targets.
+ calls #run_script otherwise. In both cases, a new job is created; the
+ rule and the variables are stored into #jobs.
- #run_script creates a shell process and stores it in #job_pids. It
increases #running_jobs.
- The child process possibly calls <b>remake</b> with a list of targets.
- When a child process ends, #server_loop calls #finalize_job, which
removes the process from #job_pids, decreases #running_jobs, and calls
#complete_job.
-- #complete_job removes the job from #job_targets and calls #update_status
+- #complete_job removes the job from #jobs and calls #update_status
to change the status of the targets. It also removes the target files in
case of failure.
*/
/**
* Global counter used to produce increasing job numbers.
- * @see job_targets
+ * @see jobs
*/
static int job_counter = 0;
*/
static std::string prefix_dir;
+/**
+ * Whether the prefix directory is different from #working_dir.
+ */
+static bool changed_prefix_dir;
+
/**
* Whether target-specific variables are propagated to prerequisites.
*/
struct stat s;
if (stat((prefix_dir + "/Remakefile").c_str(), &s) == 0)
{
- chdir(prefix_dir.c_str());
+ if (!changed_prefix_dir) return;
+ if (chdir(prefix_dir.c_str()))
+ {
+ perror("Failed to change working directory");
+ exit(EXIT_FAILURE);
+ }
+ if (show_targets)
+ {
+ std::cout << "remake: Entering directory `" << prefix_dir << '\'' << std::endl;
+ }
return;
}
size_t pos = prefix_dir.find_last_of('/');
exit(EXIT_FAILURE);
}
prefix_dir.erase(pos);
+ changed_prefix_dir = true;
}
}
/**
* Read a (possibly quoted) word.
*/
-static std::string read_word(std::istream &in)
+static std::string read_word(std::istream &in, bool detect_equal = true)
{
- int c = in.get();
+ int c = in.peek();
std::string res;
if (!in.good()) return res;
- char const *separators = " \t\r\n:$(),=+\"";
+ char const *separators = " \t\r\n$(),:";
bool quoted = c == '"';
- if (!quoted)
- {
- if (strchr(separators, c))
- {
- in.putback(c);
- return res;
- }
- res += c;
- }
+ if (quoted) in.ignore(1);
+ bool plus = false;
while (true)
{
- c = in.get();
+ c = in.peek();
if (!in.good()) return res;
if (quoted)
{
+ in.ignore(1);
if (c == '\\')
res += in.get();
else if (c == '"')
- return res;
+ quoted = false;
else
res += c;
+ continue;
}
- else
+ if (detect_equal && c == '=')
{
- if (strchr(separators, c))
- {
- in.putback(c);
- return res;
- }
- res += c;
+ if (plus) in.putback('+');
+ return res;
}
+ if (plus)
+ {
+ res += '+';
+ plus = false;
+ }
+ if (strchr(separators, c)) return res;
+ in.ignore(1);
+ if (detect_equal && c == '+') plus = true;
+ else res += c;
}
}
switch (expect_token(in, Word | Dollarpar))
{
case Word:
- res = read_word(in);
+ res = read_word(in, false);
return Success;
case Dollarpar:
{
- std::string name = read_word(in);
+ std::string name = read_word(in, false);
if (name.empty()) return SyntaxError;
if (expect_token(in, Rightpar))
nested = new variable_generator(name, local_variables);
}
rule.script = buf.str();
+ // Register phony targets.
+ if (rule.targets.front() == ".PHONY")
+ {
+ for (string_list::const_iterator i = rule.deps.begin(),
+ i_end = rule.deps.end(); i != i_end; ++i)
+ {
+ status[*i].status = Todo;
+ }
+ return;
+ }
+
// Add generic rules to the correct set.
if (generic)
{
*/
static void complete_job(int job_id, bool success)
{
- DEBUG_open << "Completing job " << job_id << "... ";
+ DEBUG << "Completing job " << job_id << '\n';
job_map::iterator i = jobs.find(job_id);
assert(i != jobs.end());
string_list const &targets = i->second.rule.targets;
if (success)
{
+ if (show_targets) std::cout << "Finished";
for (string_list::const_iterator j = targets.begin(),
j_end = targets.end(); j != j_end; ++j)
{
update_status(*j);
+ if (show_targets) std::cout << ' ' << *j;
}
+ if (show_targets) std::cout << std::endl;
}
else
{
- DEBUG_close << "failed\n";
std::cerr << "Failed to build";
for (string_list::const_iterator j = targets.begin(),
j_end = targets.end(); j != j_end; ++j)
*/
static status_e run_script(int job_id, job_t const &job)
{
- if (show_targets)
- {
- std::cout << "Building";
- for (string_list::const_iterator i = job.rule.targets.begin(),
- i_end = job.rule.targets.end(); i != i_end; ++i)
- {
- std::cout << ' ' << *i;
- }
- std::cout << std::endl;
- }
-
ref_ptr<dependency_t> dep;
dep->targets = job.rule.targets;
dep->deps.insert(job.rule.deps.begin(), job.rule.deps.end());
+ if (show_targets) std::cout << "Building";
for (string_list::const_iterator i = job.rule.targets.begin(),
i_end = job.rule.targets.end(); i != i_end; ++i)
{
dependencies[*i] = dep;
+ if (show_targets) std::cout << ' ' << *i;
}
+ if (show_targets) std::cout << std::endl;
std::string script = prepare_script(job);
free(socket_name);
#endif
save_dependencies();
+ if (show_targets && changed_prefix_dir)
+ {
+ std::cout << "remake: Leaving directory `" << prefix_dir << '\'' << std::endl;
+ }
exit(build_failure ? EXIT_FAILURE : EXIT_SUCCESS);
}
*/
int main(int argc, char *argv[])
{
- init_working_dir();
-
std::string remakefile;
string_list targets;
bool literal_targets = false;
continue;
}
new_target:
- targets.push_back(normalize(arg, working_dir, working_dir));
+ targets.push_back(arg);
DEBUG << "New target: " << arg << '\n';
}
}
+ init_working_dir();
+ normalize_list(targets, working_dir, working_dir);
+
if (indirect_targets)
{
load_dependencies(std::cin);