37 #include <Common/Version.h>
39 #include <boost/algorithm/string.hpp>
55 #include <sys/types.h>
59 #if defined(__APPLE__)
60 extern char **environ;
63 #define TASK_COLUMN_WIDTH 28
64 #define DESCRIPTION_COLUMN_WIDTH (79-TASK_COLUMN_WIDTH)
72 string extract_short_description(
const string &description) {
73 string short_description;
75 for (ptr = description.c_str(); *ptr; ptr++) {
76 if (*ptr ==
'.' && (*(ptr+1) == 0 || isspace(*(ptr+1))))
80 short_description = description.substr(0, ptr - description.c_str());
83 return short_description;
86 void extract_long_description(
const string &description, vector<string> &lines) {
88 string long_description;
90 const char *base = description.c_str();
91 const char *end = base + description.length();
95 while (*base && isspace(*base))
98 if ((end-base) > 79) {
100 for (
size_t i=0; i<20; i++) {
102 lines.push_back(
string(base, ptr-base));
110 while (*ptr && !isspace(*ptr))
112 lines.push_back(
string(base, ptr-base));
117 lines.push_back(base);
124 const string construct_with_function(set<string> &roles) {
125 string text =
"with () {\n";
126 text.append(
" local args=(\"$@\")\n");
127 text.append(
" if [ ${#args[@]} != 2 ]; then\n");
128 text.append(
" exit 0\n");
129 text.append(
" fi\n");
130 if (!roles.empty()) {
131 text.append(
" IFS=',' read -ra ROLES <<< \"${args[0]}\"\n");
132 text.append(
" ON=\n");
133 text.append(
" for role in \"${ROLES[@]}\"; do\n");
134 text.append(
" if [ $role == \"all\" ]; then\n");
135 text.append(
" ON=\"");
136 for (
auto & role : roles)
137 text.append(
format(
"(${ROLE_%s}) ", role.c_str()));
139 for (
auto & role : roles) {
140 text.append(
format(
" elif [ $role == \"%s\" ]; then\n", role.c_str()));
141 text.append(
format(
" ON=\"$ON (${ROLE_%s})\"\n", role.c_str()));
143 text.append(
" fi\n");
144 text.append(
" done\n");
145 text.append(
format(
" %s/bin/ht ssh \" $ON\" \"${args[1]}\"\n",
155 struct passwd *pw = getpwuid(getuid());
177 time_t definition_modification_time = statbuf.st_mtime;
180 cout <<
"stat('" <<
m_output_script <<
"') - " << strerror(errno) << endl;
183 time_t script_modification_time = statbuf.st_mtime;
190 if (!output_script_file.is_open())
193 while (getline(output_script_file, line)) {
195 if (line.empty() || line[0] !=
'#')
198 base = line.c_str() + 1;
199 ptr = strchr(base,
':');
201 string tag(base, ptr-base);
203 if (tag.compare(
"version") == 0) {
209 else if (tag.compare(
"dependency") == 0) {
210 string dependency_file(ptr+1);
211 boost::trim(dependency_file);
212 if (stat(dependency_file.c_str(), &statbuf) < 0)
214 if (statbuf.st_mtime > definition_modification_time)
215 definition_modification_time = statbuf.st_mtime;
220 return definition_modification_time > script_modification_time;
230 cout <<
"mkdirs('" << script_directory <<
"') - " << strerror(errno) << endl;
235 map<string, string> environ_map;
236 for (
size_t i=0; environ[i]; i++) {
237 const char *ptr = strchr(environ[i],
'=');
239 environ_map[string(environ[i], ptr-environ[i])] = string(ptr+1);
242 stack<TokenizerPtr> definitions;
252 header.append(
"#!/bin/bash\n");
253 header.append(
"#\n");
256 while (!definitions.empty()) {
257 while (definitions.top()->next(token)) {
259 output.append(token.
translator->translate(context));
261 string include_file = token.
text.substr(token.
text.find_first_of(
"include:")+8);
262 boost::trim_if(include_file, boost::is_any_of(
"'\" \t\n\r"));
266 if (include_file[0] !=
'/')
267 include_file = definitions.top()->dirname() +
"/" + include_file;
268 definitions.push( make_shared<Tokenizer>(include_file) );
278 output.append(
"CLUSTER_BUILTIN_display_line () {\n");
279 output.append(
" let size=$1\n");
280 output.append(
" for ((i=0; i<$size; i++)); do\n");
281 output.append(
" echo -n \"=\"\n");
282 output.append(
" done\n");
283 output.append(
" echo\n");
284 output.append(
"}\n");
288 output.append(
"show_variables () {\n");
289 output.append(
" echo\n");
290 for (
auto & entry : context.
symbols) {
291 output.append(
" echo \"");
292 output.append(entry.first);
293 output.append(
"=${");
294 output.append(entry.first);
295 output.append(
"}\"\n");
297 output.append(
" echo\n");
298 output.append(
"}\n");
301 output.append(construct_with_function(context.
roles));
303 const char *dots =
"....................................................";
305 output.append(
"if [ $1 == \"-T\" ] || [ $1 == \"--tasks\" ]; then\n");
306 output.append(
" echo\n");
307 output.append(
" echo \"TASK DESCRIPTION\"\n");
308 output.append(
" echo \"============================= =================================================\"\n");
309 for (
auto & entry : context.
tasks) {
310 output.append(
" echo \"");
311 output.append(entry.first);
318 output.append(extract_short_description(entry.second));
319 output.append(
"\"\n");
321 output.append(
" echo\n");
322 output.append(
" exit 0\n");
323 output.append(
"elif [ $1 == \"-e\" ] || [ $1 == \"--explain\" ]; then\n");
324 output.append(
" shift\n");
325 output.append(
" if [ $# -eq 0 ]; then\n");
326 output.append(
" echo \"Missing task name in -e option\"\n");
327 output.append(
" exit 1\n");
328 output.append(
" fi\n");
330 if (!context.
tasks.empty()) {
331 vector<string> lines;
333 for (
auto & entry : context.
tasks) {
335 output.append(
" if [ $1 == \"");
339 output.append(
" elif [ $1 == \"");
340 output.append(entry.first);
341 output.append(
"\" ]; then\n");
342 output.append(
" echo\n");
343 output.append(
" echo \"$1\"\n");
344 output.append(
" CLUSTER_BUILTIN_display_line ${#1}\n");
345 extract_long_description(entry.second, lines);
346 for (
auto & line : lines) {
347 output.append(
" echo \"");
349 output.append(
"\"\n");
352 output.append(
" echo \"-- ROLES: all\"\n");
354 output.append(
" echo \"-- ROLES: ");
355 output.append(context.
task_roles[entry.first]);
356 output.append(
"\"\n");
359 output.append(
" else\n");
360 output.append(
" echo \"Task '$1' is not defined.\"\n");
361 output.append(
" exit 1\n");
362 output.append(
" fi\n");
363 output.append(
" echo\n");
364 output.append(
" exit 0\n");
367 output.append(
" echo \"Task '$1' is not defined.\"\n");
368 output.append(
" exit 1\n");
371 output.append(
"fi\n");
374 if (!context.
tasks.empty()) {
376 for (
auto & entry : context.
tasks) {
378 output.append(
format(
"if [ $1 == \"%s\" ]; then\n shift\n %s $@\n",
379 entry.first.c_str(), entry.first.c_str()));
383 output.append(
format(
"elif [ $1 == \"%s\" ]; then\n shift\n %s $@\n",
384 entry.first.c_str(), entry.first.c_str()));
386 output.append(
"elif [ $1 == \"show_variables\" ]; then\n show_variables\n");
387 output.append(
"elif [ $1 == \"with\" ]; then\n shift\n with \"$@\"\n");
388 output.append(
"else\n");
389 output.append(
" echo \"Task '$1' is not defined.\"\n");
390 output.append(
" exit 1\n");
391 output.append(
"fi\n");
394 output.append(
"if [ $1 == \"show_variables\" ]; then\n show_variables\n");
395 output.append(
"elif [ $1 == \"with\" ]; then\n shift\n with \"$@\"\n");
396 output.append(
"else\n");
397 output.append(
" echo \"Task '$1' is not defined.\"\n");
398 output.append(
" exit 1\n");
399 output.append(
"fi\n");
407 << strerror(errno) << endl;
bool compilation_needed()
Determines if script needs to be re-built.
Retrieves system information (hardware, installation directory, etc)
TranslatorPtr translator
Translator object for token text.
map< string, string > task_roles
String format(const char *fmt,...)
Returns a String using printf like format facilities Vanilla snprintf is about 1.5x faster than this...
static ssize_t write(const String &fname, const std::string &contents)
Writes a String buffer to a file; the file is overwritten if it already exists.
set< string > roles
Set of role names.
string m_definition_file
Cluster definition file.
static bool exists(const String &fname)
Checks if a file or directory exists.
static bool mkdirs(const String &dirname)
Creates a directory (with all parent directories, if required)
Cluster definition file token.
#define TASK_COLUMN_WIDTH
File system utility functions.
void make()
Compiles the definition file into a task execution script.
Logging routines and macros.
Compatibility Macros for C/C++.
Declarations for Compiler.
map< string, string > symbols
Map of variable names to default values.
static String install_dir
The installation directory.
string m_output_script
Output script.
A String class based on std::string.
Compiler(const string &fname)
Constructor.
Context used to translate cluster definition statements.
Declarations for Tokenizer.
Cluster definition file translation definitions.
#define DESCRIPTION_COLUMN_WIDTH
const char * version_string()
map< string, string > tasks
Map of tasks to descriptive text.