1 //
2 // aegis - project change supervisor
3 // Copyright (C) 1999-2008, 2011, 2012 Peter Miller
4 // Copyright (C) 2005-2008, 2010 Walter Franzini
5 //
6 // This program is free software; you can redistribute it and/or modify
7 // it under the terms of the GNU General Public License as published by
8 // the Free Software Foundation; either version 3 of the License, or (at
9 // your option) any later version.
10 //
11 // This program is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 // General Public License for more details.
15 //
16 // You should have received a copy of the GNU General Public License
17 // along with this program. If not, see <http://www.gnu.org/licenses/>.
18 //
19
20 #include <common/ac/assert.h>
21 #include <common/ac/stddef.h>
22 #include <common/ac/stdlib.h>
23 #include <common/ac/string.h>
24 #include <common/ac/unistd.h>
25
26 #include <common/error.h>
27 #include <common/nstring.h>
28 #include <common/str.h>
29 #include <common/str_list.h>
30 #include <common/symtab/template.h>
31 #include <common/trace.h>
32 #include <common/uuidentifier.h>
33 #include <common/version_stmp.h>
34 #include <libaegis/arglex/change.h>
35 #include <libaegis/arglex/project.h>
36 #include <libaegis/attribute.h>
37 #include <libaegis/cattr.fmtgen.h>
38 #include <libaegis/change/attributes.h>
39 #include <libaegis/change/branch.h>
40 #include <libaegis/change/file.h>
41 #include <libaegis/change.h>
42 #include <libaegis/fattr.fmtgen.h>
43 #include <libaegis/file.h>
44 #include <libaegis/help.h>
45 #include <libaegis/input/cpio.h>
46 #include <libaegis/meta_parse.h>
47 #include <libaegis/move_list.h>
48 #include <libaegis/os.h>
49 #include <libaegis/output/bit_bucket.h>
50 #include <libaegis/output/file.h>
51 #include <libaegis/patch/list.h>
52 #include <libaegis/project/file.h>
53 #include <libaegis/project/file/trojan.h>
54 #include <libaegis/project.h>
55 #include <libaegis/project/history.h>
56 #include <libaegis/project/invento_walk.h>
57 #include <libaegis/sub.h>
58 #include <libaegis/undo.h>
59 #include <libaegis/user.h>
60
61 #include <aedist/usage.h>
62 #include <aedist/arglex3.h>
63 #include <aedist/change/functor/invent_build.h>
64 #include <aedist/open.h>
65 #include <aedist/receive.h>
66
67
68 static void
create_files(const nstring & cmd,cstate_src_list_ty * files,const nstring & dir,bool use_uuid)69 create_files(const nstring &cmd, cstate_src_list_ty *files, const nstring &dir,
70 bool use_uuid)
71 {
72 assert(files);
73
74 for (size_t i = 0; i < files->length; ++i)
75 {
76 nstring tail;
77
78 tail += " " + nstring(files->list[i]->file_name);
79 if (use_uuid && files->list[i]->uuid)
80 tail += " -uuid=" + nstring(files->list[i]->uuid);
81 else
82 tail += " -not-uuid";
83
84 os_become_orig();
85 os_execute(cmd + tail, OS_EXEC_FLAG_INPUT, dir);
86 os_become_undo();
87 }
88 }
89
90 static void
cstate_src_list_push_back(cstate_src_list_ty * l,cstate_src_ty * src_data)91 cstate_src_list_push_back(cstate_src_list_ty *l, cstate_src_ty *src_data)
92 {
93 meta_type *type_p = 0;
94 cstate_src_ty **dst_data_p;
95 cstate_src_ty *dst_data;
96
97 assert(l);
98 assert(src_data);
99
100 dst_data_p = (cstate_src_ty **)cstate_src_list_type.list_parse(l, &type_p);
101 assert(type_p == &cstate_src_type);
102 dst_data = (cstate_src_ty *)cstate_src_type.alloc();
103 *dst_data_p = dst_data;
104
105 //
106 // Copy only file_name and UUID since it's all we need now.
107 //
108 dst_data->file_name = str_copy(src_data->file_name);
109 if (src_data->uuid)
110 dst_data->uuid = str_copy(src_data->uuid);
111 }
112
113 static void
cstate_src_list_push_back_unique(cstate_src_list_ty * l,cstate_src_ty * src_data)114 cstate_src_list_push_back_unique(cstate_src_list_ty *l, cstate_src_ty *src_data)
115 {
116 assert(l);
117 assert(src_data);
118
119 for (size_t i = 0; i < l->length; ++i)
120 {
121 if (str_equal(src_data->file_name, l->list[i]->file_name))
122 return;
123 }
124
125 cstate_src_list_push_back(l, src_data);
126 }
127
128 static void
move_xargs(const nstring & project_name,long change_number,const nstring_list & files,const nstring & dd,const nstring & trace_options)129 move_xargs(const nstring &project_name, long change_number,
130 const nstring_list &files, const nstring &dd, const nstring &trace_options)
131 {
132 if (0 != (files.size() % 2))
133 this_is_a_bug();
134
135 nstring command =
136 nstring::format
137 (
138 "aegis --move-file --project=%s --change=%ld%s --verbose",
139 project_name.c_str(),
140 magic_zero_decode(change_number),
141 trace_options.c_str()
142 );
143
144 //
145 // aemv needs an even number of file names, the os_xargs function
146 // run the commands with, at most, 50 args. Thus each aemv
147 // invocation get an even number of file arguments.
148 //
149 os_xargs(command, files, dd);
150 }
151
152
153 static void
change_uuid_set(const nstring & project_name,long change_number,const nstring & uuid,const nstring & trace_options,const nstring & dd)154 change_uuid_set(const nstring &project_name, long change_number,
155 const nstring &uuid, const nstring &trace_options, const nstring &dd)
156 {
157 nstring s =
158 nstring::format
159 (
160 "aegis --change-attr --uuid %s -change=%ld --project=%s%s",
161 uuid.quote_shell().c_str(),
162 magic_zero_decode(change_number),
163 project_name.c_str(),
164 trace_options.c_str()
165 );
166 os_become_orig();
167 int err = os_execute_retcode(s, OS_EXEC_FLAG_INPUT, dd);
168 os_become_undo();
169
170 //
171 // If we failed to set the change's UUID then we save its value
172 // using the user defined attribute original-UUID. It's bad to
173 // throw away UUIDs.
174 //
175 if (err)
176 {
177 nstring at = nstring::format("%s+=%s", ORIGINAL_UUID, uuid.c_str());
178 nstring s2 =
179 nstring::format
180 (
181 "aegis -change-attr %s -change=%ld -project=%s%s",
182 at.quote_shell().c_str(),
183 magic_zero_decode(change_number),
184 project_name.c_str(),
185 trace_options.c_str()
186 );
187 os_become_orig();
188 os_execute(s2, OS_EXEC_FLAG_INPUT | OS_EXEC_FLAG_ERROK, dd);
189 os_become_undo();
190 }
191 }
192
193
194 static long
number_of_files(string_ty * project_name,long change_number)195 number_of_files(string_ty *project_name, long change_number)
196 {
197 project *pp;
198 change::pointer cp;
199 long result;
200
201 pp = project_alloc(project_name);
202 pp->bind_existing();
203 cp = change_alloc(pp, change_number);
204 change_bind_existing(cp);
205 result = change_file_count(cp);
206 change_free(cp);
207 project_free(pp);
208 return result;
209 }
210
211
212 static fstate_src_ty *
project_file_find_by_meta2(project * pp,cstate_src_ty * c_src_data,view_path_ty vp)213 project_file_find_by_meta2(project *pp, cstate_src_ty *c_src_data,
214 view_path_ty vp)
215 {
216 trace(("project_file_find_by_meta2(pp = %08lX, c_src = %08lX, vp = %s)\n"
217 "{\n", (long)pp, (long)c_src_data, view_path_ename(vp)));
218 trace
219 ((
220 "change: %s %s \"%s\" %s\n", file_usage_ename(c_src_data->usage),
221 file_action_ename(c_src_data->action),
222 c_src_data->file_name->str_text,
223 (c_src_data->edit_number ?
224 c_src_data->edit_number->str_text : "")
225 ));
226 #ifdef DEBUG
227 if (c_src_data->move)
228 trace((" move \"%s\"\n", c_src_data->move->str_text));
229 #endif
230
231 //
232 // Try to find the file by its UUID.
233 //
234 // For backwards compatibility reasons (UUIDs were introduced in
235 // 4.17) there could be a rename chain (pointing at UUID-less
236 // entries) to be taken into account.
237 //
238 if (c_src_data->uuid)
239 {
240 trace(("uuid = %s\n", c_src_data->uuid->str_text));
241 fstate_src_ty *p_src_data =
242 pp->file_find_by_uuid(c_src_data->uuid, vp);
243 while (p_src_data)
244 {
245 if (p_src_data->action != file_action_remove || !p_src_data->move)
246 {
247 trace
248 ((
249 "project: %s %s \"%s\" %s %s\n",
250 file_usage_ename(p_src_data->usage),
251 file_action_ename(p_src_data->action),
252 p_src_data->file_name->str_text,
253 (p_src_data->edit_origin ?
254 p_src_data->edit_origin->revision->str_text : ""),
255 (p_src_data->edit ?
256 p_src_data->edit->revision->str_text : "")
257 ));
258 trace(("return %08lX;\n", (long)p_src_data));
259 trace(("}\n"));
260 return p_src_data;
261 }
262 p_src_data = pp->file_find(p_src_data->move, vp);
263 }
264 }
265
266 //
267 // Look for the file by name, tracking renames as they happen.
268 //
269 string_ty *name = c_src_data->file_name;
270 if (c_src_data->action == file_action_create && c_src_data->move)
271 name = c_src_data->move;
272 for (;;)
273 {
274 fstate_src_ty *p_src_data = pp->file_find(name, vp);
275 if (!p_src_data)
276 {
277 trace(("return NULL;\n"));
278 trace(("}\n"));
279 return 0;
280 }
281 if (p_src_data->action != file_action_remove || !p_src_data->move)
282 {
283 trace
284 ((
285 "project: %s %s \"%s\" %s %s\n",
286 file_usage_ename(p_src_data->usage),
287 file_action_ename(p_src_data->action),
288 p_src_data->file_name->str_text,
289 (p_src_data->edit_origin ?
290 p_src_data->edit_origin->revision->str_text : ""),
291 (p_src_data->edit ?
292 p_src_data->edit->revision->str_text : "")
293 ));
294 trace(("return %08lX;\n", (long)p_src_data));
295 trace(("}\n"));
296 return p_src_data;
297 }
298 name = p_src_data->move;
299 }
300 }
301
302
303 static cstate_src_ty *
find_src(cstate_ty * cs,string_ty * filename)304 find_src(cstate_ty *cs, string_ty *filename)
305 {
306 assert(cs);
307 assert(cs->src);
308 for (size_t j = 0; j < cs->src->length; ++j)
309 {
310 cstate_src_ty *src = cs->src->list[j];
311 if (str_equal(src->file_name, filename))
312 return src;
313 }
314 return 0;
315 }
316
317
318 static void
wrong_file(input & ifp,const nstring & expected)319 wrong_file(input &ifp, const nstring &expected)
320 {
321 nstring message =
322 nstring::format
323 (
324 "wrong file, expected \"%s\", "
325 "maybe the sender needs to use the --compatibility=%s option",
326 expected.c_str(),
327 version_stamp()
328 );
329 ifp->fatal_error(message.c_str());
330 }
331
332
333 static void
missing_file(input_cpio * ifp,const nstring & expected)334 missing_file(input_cpio *ifp, const nstring &expected)
335 {
336 nstring message =
337 nstring::format
338 (
339 "missing file, expected \"%s\", "
340 "maybe the sender needs to use the --compatibility=%s option",
341 expected.c_str(),
342 version_stamp()
343 );
344 ifp->fatal_error(message.c_str());
345 }
346
347
348 static void
build_uuid_list(cstate_ty * csp,nstring_list & uuids)349 build_uuid_list(cstate_ty *csp, nstring_list &uuids)
350 {
351 if (csp->uuid)
352 uuids.push_back(nstring(csp->uuid));
353 attributes_list_ty *alp = csp->attribute;
354 if (!alp)
355 return;
356 for (size_t j = 0; j < alp->length; ++j)
357 {
358 attributes_ty *ap = alp->list[j];
359 assert(ap);
360 if (!ap)
361 continue;
362 assert(ap->name);
363 if (!ap->name)
364 continue;
365 assert(ap->value);
366 if (!ap->value)
367 continue;
368 if (0 == strcasecmp(ORIGINAL_UUID, ap->name->str_text))
369 uuids.push_back(nstring(ap->value));
370 }
371 }
372
373
374 static bool
nothing_has_changed(change::pointer cp,user_ty::pointer up)375 nothing_has_changed(change::pointer cp, user_ty::pointer up)
376 {
377 //
378 // First we do a quick scan to see if the can avoid comparing any
379 // files at all.
380 //
381 for (size_t j = 0; ; ++j)
382 {
383 fstate_src_ty *src = change_file_nth(cp, j, view_path_first);
384 if (!src)
385 break;
386 if (src->action != file_action_modify)
387 return false;
388 if (src->usage == file_usage_build)
389 return false;
390 }
391
392 //
393 // Compare each file in the change set with the same file in the
394 // baseline.
395 //
396 for (size_t k = 0; ; ++k)
397 {
398 fstate_src_ty *src = change_file_nth(cp, k, view_path_first);
399 if (!src)
400 break;
401
402 nstring path1(cp->file_path(src));
403 assert(!path1.empty());
404 nstring path2(project_file_path(cp->pp, src));
405 assert(!path2.empty());
406
407 up->become_begin();
408 bool different = files_are_different(path1, path2);
409 up->become_end();
410 if (different)
411 return false;
412 }
413
414 //
415 // nothing has changed
416 //
417 return true;
418 }
419
420
421 void
receive_main(void)422 receive_main(void)
423 {
424 int use_patch;
425 string_ty *project_name;
426 long change_number;
427 project *pp;
428 change::pointer cp;
429 size_t j;
430 cattr_ty *cattr_data;
431 string_ty *attribute_file_name;
432 move_list_ty files_moved;
433 int could_have_a_trojan;
434 int config_seen;
435 int uncopy;
436 int trojan;
437 string_ty *devdir;
438 int exec_mode;
439 int non_exec_mode;
440 int ignore_uuid;
441
442 project_name = 0;
443 change_number = 0;
444 trojan = -1;
445 nstring delta;
446 devdir = 0;
447 use_patch = -1;
448 ignore_uuid = -1;
449 nstring ifn;
450 nstring output_filename;
451 arglex();
452 while (arglex_token != arglex_token_eoln)
453 {
454 switch (arglex_token)
455 {
456 default:
457 generic_argument(usage);
458 continue;
459
460 case arglex_token_change:
461 arglex();
462 arglex_parse_change(&project_name, &change_number, usage);
463 continue;
464
465 case arglex_token_project:
466 arglex();
467 arglex_parse_project(&project_name, usage);
468 continue;
469
470 case arglex_token_file:
471 if (!ifn.empty())
472 duplicate_option(usage);
473 switch (arglex())
474 {
475 default:
476 option_needs_file(arglex_token_file, usage);
477 // NOTREACHED
478
479 case arglex_token_string:
480 ifn = arglex_value.alv_string;
481 break;
482
483 case arglex_token_stdio:
484 ifn = "-";
485 break;
486 }
487 break;
488
489 case arglex_token_trojan:
490 if (trojan > 0)
491 duplicate_option(usage);
492 if (trojan >= 0)
493 {
494 too_many_trojans:
495 mutually_exclusive_options
496 (
497 arglex_token_trojan,
498 arglex_token_trojan_not,
499 usage
500 );
501 }
502 trojan = 1;
503 break;
504
505 case arglex_token_trojan_not:
506 if (trojan == 0)
507 duplicate_option(usage);
508 if (trojan >= 0)
509 goto too_many_trojans;
510 trojan = 0;
511 break;
512
513 case arglex_token_delta:
514 if (!delta.empty())
515 duplicate_option(usage);
516 switch (arglex())
517 {
518 default:
519 option_needs_number(arglex_token_delta, usage);
520 // NOTREACHED
521
522 case arglex_token_number:
523 delta =
524 nstring::format(" --delta=%ld", arglex_value.alv_number);
525 break;
526
527 case arglex_token_string:
528 {
529 if (arglex_value.alv_string[0] == 0)
530 option_needs_number(arglex_token_delta, usage);
531 nstring arg = arglex_value.alv_string;
532 arg = arg.quote_shell();
533 delta = nstring::format(" --delta=%s", arg.c_str());
534 }
535 break;
536 }
537 break;
538
539 case arglex_token_directory:
540 if (devdir)
541 {
542 duplicate_option(usage);
543 // NOTREACHED
544 }
545 if (arglex() != arglex_token_string)
546 {
547 option_needs_dir(arglex_token_directory, usage);
548 // NOTREACHED
549 }
550 devdir = str_format(" --directory %s", arglex_value.alv_string);
551 break;
552
553 case arglex_token_patch:
554 if (use_patch > 0)
555 duplicate_option(usage);
556 if (use_patch >= 0)
557 {
558 too_many_patchs:
559 mutually_exclusive_options
560 (
561 arglex_token_patch,
562 arglex_token_patch_not,
563 usage
564 );
565 }
566 use_patch = 1;
567 break;
568
569 case arglex_token_patch_not:
570 if (use_patch == 0)
571 duplicate_option(usage);
572 if (use_patch >= 0)
573 goto too_many_patchs;
574 use_patch = 0;
575 break;
576
577 case arglex_token_uuid_not:
578 case arglex_token_ignore_uuid:
579 if (ignore_uuid > 0)
580 duplicate_option(usage);
581 if (ignore_uuid >= 0)
582 {
583 too_many_ignore_uuid:
584 mutually_exclusive_options
585 (
586 arglex_token_ignore_uuid,
587 arglex_token_ignore_uuid_not,
588 usage
589 );
590 }
591 ignore_uuid = 1;
592 break;
593
594 case arglex_token_uuid:
595 case arglex_token_ignore_uuid_not:
596 if (ignore_uuid == 0)
597 duplicate_option(usage);
598 if (ignore_uuid >= 0)
599 goto too_many_ignore_uuid;
600 ignore_uuid = 0;
601 break;
602
603 case arglex_token_output:
604 if (!output_filename.empty())
605 duplicate_option(usage);
606 if (arglex() != arglex_token_string)
607 {
608 option_needs_file(arglex_token_output, usage);
609 // NOTREACHED
610 }
611 output_filename = nstring(arglex_value.alv_string);
612 break;
613 }
614 arglex();
615 }
616 if (change_number && !output_filename.empty())
617 {
618 mutually_exclusive_options
619 (
620 arglex_token_change,
621 arglex_token_output,
622 usage
623 );
624 }
625
626 //
627 // Open the input file and verify the format.
628 //
629 input_cpio *cpio_p = aedist_open(ifn);
630 assert(cpio_p);
631
632 //
633 // Calculate the --reason option to use with the
634 // "aegis --new-change" command, if any.
635 //
636 nstring reason;
637 bool remote = false;
638 if (cpio_p->is_remote())
639 {
640 reason =
641 " --reason=" + ("Downloaded from " + cpio_p->name()).quote_shell();
642 remote = true;
643 }
644
645 //
646 // read the project name from the archive,
647 // and use it to default the project if not given
648 //
649 os_become_orig();
650 nstring archive_name;
651 input ifp = cpio_p->child(archive_name);
652 if (!ifp.is_open())
653 missing_file(cpio_p, "etc/project-name");
654 assert(!archive_name.empty());
655 if (archive_name != "etc/project-name")
656 wrong_file(ifp, "etc/project-name");
657 {
658 nstring s;
659 if (!ifp->one_line(s) || s.empty())
660 ifp->fatal_error("short file");
661 if (!project_name)
662 project_name = s.get_ref_copy();
663 }
664 ifp.close();
665 os_become_undo();
666
667 //
668 // locate project data
669 // (Even of we don't use it, this confirms it is a valid
670 // project name.)
671 //
672 pp = project_alloc(project_name);
673 pp->bind_existing();
674
675 //
676 // Read the change number form the archive, if it's there. Use that
677 // number (a) if the user didn't specify one on the command line,
678 // and (b) that number is available.
679 //
680 os_become_orig();
681 archive_name.clear();
682 ifp = cpio_p->child(archive_name);
683 if (!ifp.is_open())
684 missing_file(cpio_p, "etc/change-set");
685 assert(archive_name);
686 if (archive_name == "etc/change-number")
687 {
688 nstring s;
689 if (!ifp->one_line(s) || s.empty())
690 ifp->fatal_error("short file");
691 long proposed_change_number = s.to_long();
692 ifp.close();
693 os_become_undo();
694
695 //
696 // Make sure the change number is available.
697 //
698 if
699 (
700 !change_number
701 &&
702 proposed_change_number > 0
703 &&
704 !project_change_number_in_use(pp, proposed_change_number)
705 )
706 change_number = proposed_change_number;
707
708 //
709 // Start the next file, so we are in the same state as when
710 // there is no change number included.
711 //
712 archive_name.clear();
713 os_become_orig();
714 ifp = cpio_p->child(archive_name);
715 if (!ifp.is_open())
716 missing_file(cpio_p, "etc/change-set");
717 }
718 os_become_undo();
719
720 //
721 // default the change number
722 //
723 if (!change_number)
724 change_number = project_next_change_number(pp, 1);
725
726 //
727 // If the user asked for it, write the change number in the output
728 // file.
729 //
730 if (!output_filename.empty())
731 {
732 os_become_orig();
733 output::pointer ofp(output_file::open(output_filename));
734 os_become_undo();
735
736 ofp->fprintf("%ld\n", change_number);
737 }
738
739 //
740 // get the change details from the input
741 //
742 os_become_orig();
743 if (archive_name != "etc/change-set")
744 wrong_file(ifp, "etc/change-set");
745 cstate_ty *change_set = (cstate_ty *)parse_input(ifp, &cstate_type);
746 ifp.close();
747 os_become_undo();
748
749 nstring_list incoming_uuids;
750 build_uuid_list(change_set, incoming_uuids);
751
752 //
753 // Make sure we like the change set at a macro level.
754 //
755 if
756 (
757 !change_set->brief_description
758 ||
759 !change_set->description
760 ||
761 !change_set->src
762 ||
763 !change_set->src->length
764 )
765 cpio_p->fatal_error("bad change set");
766 for (j = 0; j < change_set->src->length; ++j)
767 {
768 cstate_src_ty *src_data;
769
770 src_data = change_set->src->list[j];
771 if
772 (
773 !src_data->file_name
774 ||
775 !src_data->file_name->str_length
776 ||
777 !(src_data->mask & cstate_src_action_mask)
778 ||
779 !(src_data->mask & cstate_src_usage_mask)
780 )
781 cpio_p->fatal_error("bad change info");
782 }
783
784 //
785 // We refuse to receive a change we already have because that can
786 // undo changes. If the local repository is the source of the
787 // change set, then the patch will fail to apply and the full
788 // source will be used, thus successive changes will be removed.
789 // The -ignore-uuid option is here to handle UUID clash, if any.
790 //
791 nstring branch;
792 nstring original_uuid;
793 time_t original_ipass_when = 0;
794 symtab<change> local_inventory;
795 bool include_branches = true;
796 bool all_changes = false;
797 bool ignore_original_uuid = true;
798 change_functor_inventory_builder cf(include_branches, all_changes,
799 ignore_original_uuid, pp, &local_inventory);
800 project_inventory_walk(pp, cf);
801 if (ignore_uuid <= 0)
802 {
803 if (change_set->uuid)
804 {
805 assert(universal_unique_identifier_valid(change_set->uuid));
806 change::pointer c = local_inventory.query(change_set->uuid);
807 if (c)
808 {
809 assert(c->cstate_data->uuid);
810 assert(universal_unique_identifier_valid(c->cstate_data->uuid));
811 assert(str_equal(change_set->uuid, c->cstate_data->uuid));
812
813 //
814 // run away, run away!
815 //
816 error_intl(0, i18n("change already present"));
817 return;
818 }
819 }
820
821 //
822 // We now add to the local inventory also the original-UUIDs.
823 //
824 ignore_original_uuid = false;
825 change_functor_inventory_builder cf2(include_branches, all_changes,
826 ignore_original_uuid, pp, &local_inventory);
827 project_inventory_walk(pp, cf2);
828
829 //
830 // If the user has not specified a delta to copy file from, we
831 // try to guess using the original-UUID attribute of the
832 // change, if any.
833 //
834 if (delta.empty() && change_set->attribute)
835 {
836 //
837 // Using the 'original-UUID' of the received change we look
838 // for a completed change with a matching 'uuid'.
839 //
840 for (size_t i = 0; i < change_set->attribute->length; ++i)
841 {
842 attributes_ty *current = change_set->attribute->list[i];
843 if (!current || !current->name || !current->value)
844 continue;
845 if (0 != strcasecmp(current->name->str_text, ORIGINAL_UUID))
846 continue;
847 if (!universal_unique_identifier_valid(current->value))
848 continue;
849 change::pointer ancestor =
850 local_inventory.query(current->value);
851 if (!ancestor || !ancestor->is_completed())
852 continue;
853 trace_string(current->value->str_text);
854
855 //
856 // We must keep track of the original-uuid and of the
857 // timestamp of the change completion for later use in
858 // the delta selection stage.
859 //
860 original_uuid = nstring(current->value);
861 original_ipass_when =
862 change_when_get
863 (
864 ancestor,
865 cstate_history_what_integrate_pass
866 );
867
868 //
869 // This works even if the ancestor change is
870 // in the trunk.
871 //
872 assert(ancestor->delta_number_get() > 0);
873 delta =
874 nstring::format
875 (
876 " --delta=%ld",
877 ancestor->delta_number_get()
878 );
879 trace_string(delta.c_str());
880
881 if (ancestor->pp)
882 branch = nstring(project_version_short_get(ancestor->pp));
883 if (!branch.empty())
884 branch = " --branch=" + branch;
885 else
886 branch = " --trunk";
887 trace_nstring(branch);
888 break;
889 }
890 }
891 }
892
893 //
894 // We need to work out if we are a project administartor. This lets
895 // us pass the testing atrributes through literally, rather than
896 // modulo the defaults.
897 //
898 user_ty::pointer up = user_ty::create();
899 bool am_admin = project_administrator_query(pp, up->name());
900
901 //
902 // construct change attributes from the change_set
903 //
904 // Be careful when copying across the testing exemptions, to
905 // make sure we don't ask for an exemption we can't have.
906 //
907 os_become_orig();
908 attribute_file_name = os_edit_filename(0);
909 undo_unlink_errok(attribute_file_name);
910 os_become_undo();
911 cattr_data = (cattr_ty *)cattr_type.alloc();
912 cattr_data->brief_description = str_copy(change_set->brief_description);
913 cattr_data->description = str_copy(change_set->description);
914 cattr_data->cause = change_set->cause;
915 if (am_admin)
916 {
917 //
918 // Only project administrators can do this, otherwise we risk
919 // having the "aegis --new-change" command fail, which isn't nice.
920 //
921 cattr_data->test_exempt = change_set->test_exempt;
922 cattr_data->test_baseline_exempt = change_set->test_baseline_exempt;
923 cattr_data->regression_test_exempt = change_set->regression_test_exempt;
924 }
925 else
926 {
927 cattr_ty *dflt = (cattr_ty *)cattr_type.alloc();
928 dflt->cause = change_set->cause;
929 pconf_ty *pconf_data = project_pconf_get(pp);
930 change_attributes_default(dflt, pp, pconf_data);
931
932 cattr_data->test_exempt =
933 (change_set->test_exempt && dflt->test_exempt);
934 cattr_data->test_baseline_exempt =
935 (change_set->test_baseline_exempt && dflt->test_baseline_exempt);
936 cattr_data->regression_test_exempt =
937 (
938 change_set->regression_test_exempt
939 &&
940 dflt->regression_test_exempt
941 );
942 cattr_type.free(dflt);
943 }
944 if (change_set->attribute)
945 cattr_data->attribute = attributes_list_copy(change_set->attribute);
946 if (remote)
947 {
948 if (!cattr_data->attribute)
949 cattr_data->attribute =
950 (attributes_list_ty *)attributes_list_type.alloc();
951 attributes_list_insert
952 (
953 cattr_data->attribute,
954 "foreign-copyright",
955 "true"
956 );
957 }
958
959 os_become_orig();
960 cattr_write_file(attribute_file_name, cattr_data, 0);
961 os_become_undo();
962 cattr_type.free(cattr_data);
963 cattr_data = 0;
964 project_free(pp);
965 pp = 0;
966
967 //
968 // create the new change
969 //
970 nstring trace_options(trace_args());
971 os_become_orig();
972 nstring dot(os_curdir());
973 nstring new_change_command =
974 nstring::format
975 (
976 "aegis --new-change %ld --project=%s --file=%s --verbose%s%s",
977 magic_zero_decode(change_number),
978 project_name->str_text,
979 attribute_file_name->str_text,
980 reason.c_str(),
981 trace_options.c_str()
982 );
983 os_execute(new_change_command, OS_EXEC_FLAG_INPUT, dot);
984 os_unlink_errok(attribute_file_name);
985
986 //
987 // Begin development of the new change.
988 //
989 nstring develop_begin_command =
990 nstring::format
991 (
992 "aegis --develop-begin %ld --project %s --verbose%s%s",
993 magic_zero_decode(change_number),
994 project_name->str_text,
995 (devdir ? devdir->str_text : ""),
996 trace_options.c_str()
997 );
998 os_execute(develop_begin_command, OS_EXEC_FLAG_INPUT, dot);
999 os_become_undo();
1000
1001 //
1002 // Change to the development directory, so that we can use
1003 // relative filenames. It makes things easier to read.
1004 //
1005 pp = project_alloc(project_name);
1006 pp->bind_existing();
1007 cp = change_alloc(pp, change_number);
1008 change_bind_existing(cp);
1009 nstring dd(change_development_directory_get(cp, 0));
1010 change_free(cp);
1011 cp = 0;
1012
1013 os_chdir(dd);
1014
1015 //
1016 // Adjust the file actions to ensure that renames are really
1017 // renames. Historical versions of Aegis were not as fussy as now,
1018 // and you could get half a rename by aermu or aenfu of one half.
1019 //
1020 trace(("check that renames are complete\n"));
1021 for (j = 0; j < change_set->src->length; ++j)
1022 {
1023 cstate_src_ty *src1 = change_set->src->list[j];
1024 if (!src1->move)
1025 continue;
1026 trace(("%s \"%s\", move=\"%s\"\n", file_action_ename(src1->action),
1027 src1->file_name->str_text, src1->move->str_text));
1028 cstate_src_ty *src2 = find_src(change_set, src1->move);
1029 if (!src2)
1030 {
1031 // Only half a move.
1032 trace(("half a move\n"));
1033 str_free(src1->move);
1034 src1->move = 0;
1035 continue;
1036 }
1037
1038 switch (src1->action)
1039 {
1040 case file_action_create:
1041 if (src2->action != file_action_remove)
1042 {
1043 // Something really weird happened.
1044 trace(("not a move\n"));
1045 assert(0);
1046 str_free(src1->move);
1047 src1->move = 0;
1048 }
1049 break;
1050
1051 case file_action_remove:
1052 if (src2->action != file_action_create)
1053 {
1054 //
1055 // Something happened to the file after the rename.
1056 // This can occur when the change is a branch.
1057 //
1058 trace(("corresponds to %s \"%s\"\n",
1059 file_action_ename(src2->action),
1060 src2->file_name->str_text));
1061 trace(("not a simple move\n"));
1062 //
1063 // If the target was later removed we short-circuit.
1064 // This is needed because some versions of aedist -send
1065 // erroneously represent (rename -> remove) by
1066 // (rename <--> rename).
1067 // This solution is wrong if a change_set can contain
1068 // a chain, but the sender supposedly collapses chains.
1069 //
1070 if (src2->action == file_action_remove)
1071 {
1072 str_free(src1->move);
1073 src1->move = 0;
1074 }
1075 }
1076 break;
1077
1078 case file_action_modify:
1079 case file_action_insulate:
1080 case file_action_transparent:
1081 #ifdef DEBUG
1082 default:
1083 #endif
1084 //
1085 // None of these actions are supposed to have the move field
1086 // set.
1087 //
1088 trace(("not a move\n"));
1089 assert(0);
1090 str_free(src1->move);
1091 src1->move = 0;
1092 break;
1093 }
1094 }
1095
1096 //
1097 // Adjust the file actions to reflect the current state of
1098 // the project.
1099 //
1100 trace(("adjust file actions\n"));
1101 bool need_to_test = false;
1102 could_have_a_trojan = 0;
1103 symtab<nstring> old_name;
1104 for (j = 0; j < change_set->src->length; ++j)
1105 {
1106 cstate_src_ty *src_data;
1107 fstate_src_ty *p_src_data;
1108
1109 src_data = change_set->src->list[j];
1110 assert(src_data);
1111 assert(src_data->file_name);
1112 trace(("%ld %s %s\n", (long) j, src_data->file_name->str_text,
1113 file_action_ename(src_data->action)));
1114 p_src_data =
1115 project_file_find_by_meta2(pp, src_data, view_path_extreme);
1116 switch (src_data->action)
1117 {
1118 case file_action_remove:
1119 //
1120 // Removing a removed file would be an
1121 // error, so take it out of the list completely.
1122 //
1123 trace(("remove\n"));
1124 if (!p_src_data)
1125 {
1126 trace(("no matching project file\n"));
1127 if (src_data->move)
1128 {
1129 //
1130 // does the target file exist?
1131 //
1132 trace(("which makes rename difficult\n"));
1133 p_src_data =
1134 pp->file_find(src_data->move, view_path_extreme);
1135 cstate_src_ty *src2 = find_src(change_set, src_data->move);
1136 assert(src2); // we already verified this
1137 if (p_src_data)
1138 {
1139 trace(("rewrite as modify\n"));
1140 src2->action = file_action_modify;
1141 }
1142 else
1143 {
1144 trace(("rewrite as create\n"));
1145 src2->action = file_action_create;
1146 }
1147 str_free(src2->move);
1148 src2->move = 0;
1149
1150 //
1151 // We also want to ignore the remove half of the
1152 // rename, so fall through...
1153 //
1154 }
1155
1156 //
1157 // It's tempting to fill the hole the removed element
1158 // leaves with the last list element and in doing so,
1159 // shortening the list. But that would mean the order
1160 // of the files in the change set differs from the order
1161 // of the files in the cpio archive, but this would
1162 // create problems later. We have to do it the long way
1163 // and shuffle everything down.
1164 //
1165 nuke_and_skip:
1166 trace(("nuke and skip\n"));
1167 for (size_t k = j + 1; k < change_set->src->length; ++k)
1168 change_set->src->list[k - 1] = change_set->src->list[k];
1169 cstate_src_type.free(src_data);
1170 --j;
1171 change_set->src->length--;
1172 continue;
1173 }
1174
1175 //
1176 // The view_path_extreme argument to project_find_file_by_
1177 // meta2 was supposed to take care of making removed files
1178 // disappear.
1179 //
1180 assert(p_src_data->action != file_action_remove);
1181
1182 //
1183 // Take care of removing and renaming files which don't have
1184 // the same name in the project as in the incoming change set.
1185 //
1186 if (!str_equal(src_data->file_name, p_src_data->file_name))
1187 {
1188 if (src_data->move)
1189 {
1190 if (str_equal(src_data->move, p_src_data->file_name))
1191 {
1192 //
1193 // The file has already been renamed, so there
1194 // is no need to remove it, but instead we turn
1195 // the target half into a modify action if it is
1196 // just a create action.
1197 //
1198 trace(("rename becomes modify\n"));
1199 cstate_src_ty *src2 =
1200 find_src(change_set, src_data->move);
1201 if (src2 && (src2->action == file_action_create))
1202 {
1203 assert(src2->move);
1204 assert(str_equal(src2->move,src_data->file_name));
1205 str_free(src2->move);
1206 src2->move = 0;
1207 src2->action = file_action_modify;
1208 }
1209 goto nuke_and_skip;
1210 }
1211
1212 //
1213 // We are about to update the file name in the
1214 // incoming change set to match the file name in the
1215 // project. But first we must update the create
1216 // half of the rename as well.
1217 //
1218 trace(("fix rename To half\n"));
1219 cstate_src_ty *src2 =
1220 find_src(change_set, src_data->move);
1221 assert(src2);
1222 assert(src2->move);
1223 assert(str_equal(src2->move, src_data->file_name));
1224
1225 str_free(src2->move);
1226 src2->move = str_copy(p_src_data->file_name);
1227 }
1228
1229 //
1230 // Update the file name in the incoming change set to
1231 // match the file name in the project.
1232 // Save the old file name, we will use it to look into
1233 // the archive.
1234 //
1235 trace(("file name changed\n"));
1236 old_name.assign
1237 (
1238 nstring(p_src_data->file_name),
1239 nstring(src_data->file_name)
1240 );
1241 str_free(src_data->file_name);
1242 src_data->file_name = str_copy(p_src_data->file_name);
1243 }
1244 break;
1245
1246 case file_action_transparent:
1247 trace(("transparent\n"));
1248 assert(!src_data->move);
1249 if (!p_src_data)
1250 goto nuke_and_skip;
1251
1252 //
1253 // Do we need to change the name of the file?
1254 //
1255 if (!str_equal(src_data->file_name, p_src_data->file_name))
1256 {
1257 trace(("file name changed\n"));
1258 //
1259 // Save the old file name, we will use it to look into
1260 // the archive.
1261 //
1262 old_name.assign
1263 (
1264 nstring(p_src_data->file_name),
1265 nstring(src_data->file_name)
1266 );
1267 str_free(src_data->file_name);
1268 src_data->file_name = str_copy(p_src_data->file_name);
1269 }
1270 // FIXME: do we need to check that making it transparent is
1271 // even possible in this branch?!?
1272 break;
1273
1274 case file_action_create:
1275 trace(("create\n"));
1276 if (src_data->move)
1277 {
1278 //
1279 // Is the file missing from the repository?
1280 // Has the rename already happened?
1281 //
1282 trace(("create half or rename\n"));
1283 if
1284 (
1285 !p_src_data
1286 ||
1287 str_equal(src_data->file_name, p_src_data->file_name)
1288 )
1289 {
1290 //
1291 // Alter the file action.
1292 //
1293 if (p_src_data)
1294 {
1295 trace(("rename becomes modify\n"));
1296 src_data->action = file_action_modify;
1297 }
1298 else
1299 {
1300 trace(("rename becomes create\n"));
1301 src_data->action = file_action_create;
1302 }
1303
1304
1305 //
1306 // Nuke the remove half of the rename.
1307 //
1308 // We can't just leave it for the remove half to
1309 // clean up, because it may be after this point,
1310 // and the clues are gone. Plus, it would break
1311 // the assertion that all renames have both halves
1312 // present in the file list.
1313 //
1314 size_t m = 0;
1315 for (m = 0; m < change_set->src->length; ++m)
1316 {
1317 cstate_src_ty *src2 = change_set->src->list[m];
1318 if (str_equal(src_data->move, src2->file_name))
1319 {
1320 assert(src2->action == file_action_remove);
1321 cstate_src_type.free(src2);
1322 break;
1323 }
1324 }
1325 assert(m < change_set->src->length);
1326 for (size_t k = m + 1; k < change_set->src->length; ++k)
1327 change_set->src->list[k - 1] = change_set->src->list[k];
1328 str_free(src_data->move);
1329 src_data->move = 0;
1330 //
1331 // We need to go back one step only if the remove
1332 // half was before the current file.
1333 //
1334 if (m < j)
1335 --j;
1336 change_set->src->length--;
1337 }
1338 else
1339 {
1340 //
1341 // The rename hasn't happened yet.
1342 // Do not mess with the file names.
1343 //
1344 trace(("rename is clean\n"));
1345 }
1346 }
1347 else
1348 {
1349 //
1350 // The file exists in the project. Alter the incoming
1351 // change set to modify the file.
1352 //
1353 if (p_src_data)
1354 {
1355 //
1356 // FIXME: we need to look for tests with the same
1357 // name but different UUIDs, and automagically give
1358 // them a new name, rather than simply ignoring the
1359 // UUID.
1360 //
1361 trace(("create becomes modify\n"));
1362 src_data->action = file_action_modify;
1363
1364 //
1365 // If the name is different (this only happens when
1366 // the incoming file has a UUID) make it match the
1367 // project.
1368 //
1369 if (!str_equal(src_data->file_name, p_src_data->file_name))
1370 {
1371 trace(("file name changed\n"));
1372 //
1373 // Save the old file name, we will use it to
1374 // look into the archive.
1375 //
1376 old_name.assign
1377 (
1378 nstring(p_src_data->file_name),
1379 nstring(src_data->file_name)
1380 );
1381 str_free(src_data->file_name);
1382 src_data->file_name = str_copy(p_src_data->file_name);
1383 }
1384 }
1385 }
1386 break;
1387
1388 case file_action_insulate:
1389 assert(0);
1390 // fall through...
1391
1392 case file_action_modify:
1393 trace(("modify\n"));
1394 assert(!src_data->move);
1395 if (!p_src_data)
1396 {
1397 //
1398 // The named file doesn't exist in the project, to
1399 // convert the action into a create instead.
1400 //
1401 trace(("modify becomes create\n"));
1402 src_data->action = file_action_create;
1403 }
1404 else
1405 {
1406 //
1407 // Make sure the action is a modify (it is supposed to
1408 // be impossible for an insulate action to get here, but
1409 // you never know).
1410 //
1411 src_data->action = file_action_modify;
1412
1413 //
1414 // If the name is different (this only happens when
1415 // the incoming file has a UUID) make it match the
1416 // project.
1417 //
1418 if (!str_equal(src_data->file_name, p_src_data->file_name))
1419 {
1420 trace(("file name changed\n"));
1421 //
1422 // Save the old file name, we will use it to look
1423 // into the archive.
1424 //
1425 old_name.assign
1426 (
1427 nstring(p_src_data->file_name),
1428 nstring(src_data->file_name)
1429 );
1430 str_free(src_data->file_name);
1431 src_data->file_name = str_copy(p_src_data->file_name);
1432 }
1433 }
1434 break;
1435 }
1436
1437 //
1438 // Watch out for infection vectors.
1439 //
1440 trace(("check for trojans\n"));
1441 if (project_file_trojan_suspect(pp, src_data->file_name))
1442 could_have_a_trojan = 1;
1443 }
1444
1445 //
1446 // add the removed files to the change
1447 //
1448 trace(("look for removed files\n"));
1449 move_list_constructor(&files_moved);
1450 nstring_list files_source;
1451
1452 for (j = 0; j < change_set->src->length; ++j)
1453 {
1454 cstate_src_ty *src_data;
1455 fstate_src_ty *p_src_data;
1456
1457 //
1458 // For now, we are only removing files.
1459 //
1460 src_data = change_set->src->list[j];
1461 assert(src_data);
1462 assert(src_data->file_name);
1463 p_src_data =
1464 project_file_find_by_meta2(pp, src_data, view_path_extreme);
1465 switch (src_data->action)
1466 {
1467 case file_action_remove:
1468 break;
1469
1470 case file_action_modify:
1471 assert(p_src_data);
1472 if (p_src_data && p_src_data->usage != src_data->usage)
1473 {
1474 //
1475 // When files change type, it is necessary to remove
1476 // them *and* then create them in the same change.
1477 // Make sure the create loop also creates this file.
1478 //
1479 switch (p_src_data->usage)
1480 {
1481 case file_usage_config:
1482 //
1483 // We are receiving an archive that contains a file
1484 // that our repository knows as file_usage_config,
1485 // but deleting the last config file is forbidden
1486 // and is wrong because we are receiving an old
1487 // style archive. Thus we skip the deletion.
1488 //
1489 continue;
1490
1491 case file_usage_source:
1492 case file_usage_test:
1493 case file_usage_manual_test:
1494 case file_usage_build:
1495 #ifndef DEBUG
1496 default:
1497 #endif
1498 trace(("modify becomes create\n"));
1499 src_data->action = file_action_create;
1500
1501 if (src_data->uuid)
1502 {
1503 //
1504 // Remove the UUID so that we don't try to set
1505 // it later (because we do this, later, for all
1506 // created files with UUIDs).
1507 //
1508 str_free(src_data->uuid);
1509 src_data->uuid = 0;
1510 }
1511 break;
1512 }
1513 break;
1514 }
1515 continue;
1516
1517 case file_action_create:
1518 case file_action_insulate:
1519 case file_action_transparent:
1520 #ifndef DEBUG
1521 default:
1522 #endif
1523 continue;
1524 }
1525
1526 //
1527 // add it to the list
1528 //
1529 if (src_data->move)
1530 {
1531 assert(src_data->action == file_action_remove);
1532 move_list_append_remove
1533 (
1534 &files_moved,
1535 src_data->file_name,
1536 src_data->move
1537 );
1538 }
1539 else
1540 files_source.push_back_unique(nstring(src_data->file_name));
1541 }
1542 if (!files_source.empty())
1543 {
1544 nstring s =
1545 nstring::format
1546 (
1547 "aegis --remove-file --project=%s --change=%ld%s --verbose",
1548 project_name->str_text,
1549 magic_zero_decode(change_number),
1550 trace_options.c_str()
1551 );
1552 os_xargs(s, files_source, dd);
1553 }
1554
1555 //
1556 // add the modified files to the change
1557 //
1558 trace(("look for modified files\n"));
1559 files_source.clear();
1560 symtab<nstring_list> files_source_by_origin;
1561 for (j = 0; j < change_set->src->length; ++j)
1562 {
1563 cstate_src_ty *src_data;
1564
1565 //
1566 // For now, we are only copying files.
1567 //
1568 src_data = change_set->src->list[j];
1569 assert(src_data->file_name);
1570 switch (src_data->action)
1571 {
1572 case file_action_modify:
1573 switch (src_data->usage)
1574 {
1575 case file_usage_build:
1576 break;
1577
1578 case file_usage_test:
1579 case file_usage_manual_test:
1580 need_to_test = true;
1581 // fall through...
1582
1583 case file_usage_source:
1584 case file_usage_config:
1585 //
1586 // We must initialize the origin with the information
1587 // from previous processing.
1588 //
1589 nstring origin = branch + delta;
1590
1591 //
1592 // We have selected an origin delta using the
1593 // original-UUID attribute contained in the archive.
1594 // If we are receiving a branch that also contain the
1595 // entire_source we must check that the change set
1596 // that the UUID identify contains every single file.
1597 //
1598 if (!original_uuid.empty() && !branch.empty())
1599 {
1600 change::pointer ancestor =
1601 local_inventory.query(original_uuid);
1602 assert(ancestor);
1603 fstate_src_ty *fsrc =
1604 ancestor->file_find
1605 (
1606 nstring(src_data->file_name),
1607 view_path_first
1608 );
1609 //
1610 // We reset the origin if the ancestor change does
1611 // not contain the file.
1612 //
1613 if (!fsrc)
1614 origin = "";
1615 }
1616
1617 if (src_data->attribute)
1618 {
1619 attributes_ty *edit_origin_UUID =
1620 attributes_list_find
1621 (
1622 src_data->attribute,
1623 EDIT_ORIGIN_UUID
1624 );
1625 if (edit_origin_UUID)
1626 {
1627 change::pointer ancestor =
1628 local_inventory.query(edit_origin_UUID->value);
1629
1630 if (ancestor)
1631 {
1632 assert(ancestor->delta_number_get() > 0);
1633 time_t ancestor_ipass_when =
1634 change_when_get
1635 (
1636 ancestor,
1637 cstate_history_what_integrate_pass
1638 );
1639
1640 //
1641 // We select the delta to merge with using
1642 // the most recently integrated change set.
1643 //
1644 if (ancestor_ipass_when > original_ipass_when)
1645 {
1646 //
1647 // We cannot reuse existing delta and
1648 // branch variables, which used to
1649 // contains data from previous processing,
1650 // originl-UUID or options related,
1651 // because this will cause wrong origin
1652 // selection.
1653 //
1654 nstring ancestor_delta =
1655 nstring::format
1656 (
1657 " --delta=%ld",
1658 ancestor->delta_number_get()
1659 );
1660 trace_string(ancestor_delta.c_str());
1661
1662 nstring ancestor_branch =
1663 nstring
1664 (
1665 project_version_short_get(ancestor->pp)
1666 );
1667 if (!ancestor_branch.empty())
1668 ancestor_branch =
1669 " --branch=" + ancestor_branch;
1670 else
1671 ancestor_branch = " --trunk";
1672 trace_nstring(ancestor_branch);
1673 origin = ancestor_branch + ancestor_delta;
1674 }
1675 }
1676 }
1677 }
1678
1679 //
1680 // We create a hash table with origin as the key and
1681 // with the list of files to be copied as the value.
1682 //
1683 nstring_list *file_list =
1684 files_source_by_origin.query(origin);
1685 if (!file_list)
1686 {
1687 file_list = new nstring_list;
1688 files_source_by_origin.assign(origin, file_list);
1689 }
1690 file_list->push_back_unique(nstring(src_data->file_name));
1691 break;
1692 }
1693 break;
1694
1695 case file_action_create:
1696 case file_action_remove:
1697 case file_action_transparent:
1698 break;
1699
1700 case file_action_insulate:
1701 #ifndef DEBUG
1702 default:
1703 #endif
1704 assert(0);
1705 break;
1706 }
1707
1708 //
1709 // FIXME: we need to look for tests with the same name but
1710 // different UUIDs, and automagically give them a new name,
1711 // rather than simply ignoring the UUID.
1712 //
1713 }
1714 uncopy = 0;
1715
1716 //
1717 // We copy modified files from the baseline grouping them using
1718 // the origin.
1719 //
1720 if (!files_source_by_origin.empty())
1721 {
1722 uncopy = 1;
1723 nstring_list origin;
1724
1725 files_source_by_origin.keys(origin);
1726 assert(!origin.empty());
1727 for (size_t c = 0; c < origin.size(); ++c)
1728 {
1729 nstring_list *files_list =
1730 files_source_by_origin.query(origin[c]);
1731 assert(!files_list->empty());
1732 nstring s =
1733 nstring::format
1734 (
1735 "aegis --copy-file --project=%s --change=%ld%s "
1736 "--verbose%s",
1737 project_name->str_text,
1738 magic_zero_decode(change_number),
1739 trace_options.c_str(),
1740 origin[c].c_str()
1741 );
1742 os_xargs(s, *files_list, dd);
1743 }
1744 }
1745
1746 //
1747 // add the new files to the change
1748 //
1749 trace(("look for created files\n"));
1750 files_source.clear();
1751 cstate_src_list_ty *files_source2 =
1752 (cstate_src_list_ty *)cstate_src_list_type.alloc();
1753 cstate_src_list_ty *files_config =
1754 (cstate_src_list_ty *)cstate_src_list_type.alloc();
1755 cstate_src_list_ty *files_build =
1756 (cstate_src_list_ty *)cstate_src_list_type.alloc();
1757 cstate_src_list_ty *files_test_auto =
1758 (cstate_src_list_ty *)cstate_src_list_type.alloc();
1759 cstate_src_list_ty *files_test_manual =
1760 (cstate_src_list_ty *)cstate_src_list_type.alloc();
1761 nstring the_config_file_old(THE_CONFIG_FILE_OLD);
1762 for (j = 0; j < change_set->src->length; ++j)
1763 {
1764 cstate_src_ty *src_data;
1765
1766 //
1767 // for now, we are only dealing with create
1768 //
1769 src_data = change_set->src->list[j];
1770 assert(src_data->file_name);
1771 switch (src_data->action)
1772 {
1773 case file_action_create:
1774 break;
1775
1776 case file_action_modify:
1777 case file_action_remove:
1778 case file_action_transparent:
1779 continue;
1780
1781 case file_action_insulate:
1782 #ifndef DEBUG
1783 default:
1784 #endif
1785 assert(0);
1786 continue;
1787 }
1788
1789 //
1790 // add it to the list
1791 //
1792 if (src_data->move)
1793 {
1794 assert(src_data->action == file_action_create);
1795 move_list_append_create
1796 (
1797 &files_moved,
1798 src_data->move,
1799 src_data->file_name
1800 );
1801 continue;
1802 }
1803 switch (src_data->usage)
1804 {
1805 case file_usage_source:
1806 if (the_config_file_old == nstring(src_data->file_name))
1807 {
1808 cstate_src_list_push_back_unique(files_config, src_data);
1809 continue;
1810 }
1811 cstate_src_list_push_back_unique(files_source2, src_data);
1812 break;
1813
1814 case file_usage_config:
1815 cstate_src_list_push_back_unique(files_config, src_data);
1816 break;
1817
1818 case file_usage_build:
1819 cstate_src_list_push_back_unique(files_build, src_data);
1820 break;
1821
1822 case file_usage_test:
1823 cstate_src_list_push_back_unique(files_test_auto, src_data);
1824 need_to_test = true;
1825 break;
1826
1827 case file_usage_manual_test:
1828 cstate_src_list_push_back_unique(files_test_manual, src_data);
1829 need_to_test = true;
1830 break;
1831 }
1832 }
1833
1834 if (files_build->length > 0)
1835 {
1836 nstring s =
1837 nstring::format
1838 (
1839 "aegis --new-file --build --project=%s --change=%ld%s "
1840 "--verbose --no-template ",
1841 project_name->str_text,
1842 magic_zero_decode(change_number),
1843 trace_options.c_str()
1844 );
1845 create_files(s, files_build, dd, ignore_uuid <= 0);
1846 }
1847 if (files_test_auto->length)
1848 {
1849 nstring s =
1850 nstring::format
1851 (
1852 "aegis --new-test --automatic --project=%s --change=%ld%s "
1853 "--verbose --no-template",
1854 project_name->str_text,
1855 magic_zero_decode(change_number),
1856 trace_options.c_str()
1857 );
1858
1859 create_files(s, files_test_auto, dd, ignore_uuid <= 0);
1860 }
1861 if (files_test_manual->length)
1862 {
1863 nstring s =
1864 nstring::format
1865 (
1866 "aegis --new-test --manual --project=%s --change=%ld%s "
1867 "--verbose --no-template",
1868 project_name->str_text,
1869 magic_zero_decode(change_number),
1870 trace_options.c_str()
1871 );
1872 create_files(s, files_test_manual, dd, ignore_uuid <= 0);
1873 }
1874 if (files_source2->length)
1875 {
1876 nstring new_file_command =
1877 nstring::format
1878 (
1879 "aegis --new-file --project=%s --change=%ld%s --verbose "
1880 "--no-template",
1881 project_name->str_text,
1882 magic_zero_decode(change_number),
1883 trace_options.c_str()
1884 );
1885 create_files(new_file_command, files_source2, dd, ignore_uuid <= 0);
1886 }
1887
1888 //
1889 // NOTE: Create project configuration files one last, in case the
1890 // incoming change set includes the first instance of the project
1891 // configuration file. We don't want errors or inconsistencies in
1892 // the project configuration files preventing us from sucessfully
1893 // unpacking the change set.
1894 //
1895 if (files_config->length)
1896 {
1897 nstring s =
1898 nstring::format
1899 (
1900 "aegis --new-file --config --project=%s --change=%ld%s "
1901 "--verbose --no-template",
1902 project_name->str_text,
1903 magic_zero_decode(change_number),
1904 trace_options.c_str()
1905 );
1906 create_files(s, files_config, dd, ignore_uuid <= 0);
1907 }
1908
1909 //
1910 // Now cope with files which moved.
1911 // They get a command each.
1912 //
1913 nstring_list batch_moved;
1914 for (j = 0; j < files_moved.length; ++j)
1915 {
1916 move_ty *mp = files_moved.item + j;
1917 trace(("from=\"%s\"\n", mp->from ? mp->from->str_text : ""));
1918 trace(("remove=%d\n", mp->remove));
1919 trace(("to=\"%s\"\n", mp->to ? mp->to->str_text : ""));
1920 trace(("create=%d\n", mp->create));
1921 assert(mp->create);
1922 assert(mp->remove);
1923 assert(mp->from);
1924 assert(mp->to);
1925 batch_moved.push_back(nstring(mp->from));
1926 batch_moved.push_back(nstring(mp->to));
1927 }
1928 move_xargs
1929 (
1930 nstring(project_name),
1931 change_number,
1932 batch_moved,
1933 dd,
1934 trace_options
1935 );
1936 move_list_destructor(&files_moved);
1937
1938 //
1939 // now extract each file from the input
1940 //
1941 trace(("extract each file\n"));
1942 cp = change_alloc(pp, change_number);
1943 change_bind_existing(cp);
1944 os_become_orig();
1945 for (j = 0; j < change_set->src->length; ++j)
1946 {
1947 cstate_src_ty *src_data;
1948 output::pointer ofp;
1949 int need_whole_source;
1950
1951 // verbose progress message here?
1952 src_data = change_set->src->list[j];
1953 trace_string(src_data->file_name->str_text);
1954 switch (src_data->action)
1955 {
1956 case file_action_insulate:
1957 case file_action_remove:
1958 case file_action_transparent:
1959 #ifndef DEBUG
1960 default:
1961 #endif
1962 continue;
1963
1964 case file_action_create:
1965 case file_action_modify:
1966 break;
1967 }
1968 assert(src_data->file_name);
1969
1970 //
1971 // If the file in the change set has been renamed to match the
1972 // local project we must look in the archive using the old
1973 // name, not the new one.
1974 //
1975 nstring *old = old_name.query(src_data->file_name);
1976
1977 switch (src_data->usage)
1978 {
1979 case file_usage_build:
1980 continue;
1981
1982 case file_usage_config:
1983 case file_usage_test:
1984 case file_usage_manual_test:
1985 could_have_a_trojan = 1;
1986 break;
1987
1988 case file_usage_source:
1989 break;
1990 }
1991 archive_name.clear();
1992 ifp = cpio_p->child(archive_name);
1993 if (!ifp.is_open())
1994 {
1995 missing_file
1996 (
1997 cpio_p,
1998 nstring::format
1999 (
2000 "patch/%s",
2001 old ? old->c_str() : src_data->file_name->str_text
2002 )
2003 );
2004 }
2005 assert(archive_name);
2006 need_whole_source = 1;
2007
2008 if (archive_name.starts_with("patch/"))
2009 {
2010 //
2011 // Check to see we got the file we expected
2012 //
2013 {
2014 nstring s1 =
2015 nstring::format("patch/%s", src_data->file_name->str_text);;
2016 nstring s2;
2017 if (old)
2018 s2 = "patch/" + *old;
2019 assert(archive_name == s1 || archive_name == s2);
2020 }
2021
2022 bool patch_is_empty = (ifp->length() == 0);
2023
2024 //
2025 // Put the patch into a file, so that we can invoke the
2026 // patch command. It is safe to assume that the patch is in
2027 // the correct format to use the patch -p0 option.
2028 //
2029 nstring patch_file_name = nstring(os_edit_filename(0));
2030 if (!patch_is_empty)
2031 {
2032 output::pointer os = output_file::binary_open(patch_file_name);
2033 os << ifp;
2034 }
2035 ifp.close();
2036
2037 //
2038 // We have a patch file, but we also know that a
2039 // complete source follows. We can apply the patch
2040 // or discard it. If we fail to apply it cleanly,
2041 // we can always use the complete source which follows.
2042 //
2043 switch (src_data->action)
2044 {
2045 case file_action_remove:
2046 case file_action_insulate:
2047 case file_action_transparent:
2048 break;
2049
2050 case file_action_create:
2051 //
2052 // We handle only the create half of the rename.
2053 //
2054 if (!src_data->move)
2055 break;
2056 // Fall through...
2057
2058 case file_action_modify:
2059 if (!use_patch)
2060 break;
2061
2062 //
2063 // Empty patches always apply cleanly.
2064 //
2065 if (patch_is_empty)
2066 {
2067 need_whole_source = 0;
2068 break;
2069 }
2070
2071 //
2072 // Apply the patch.
2073 //
2074 nstring command =
2075 nstring::format
2076 (
2077 CONF_PATCH " -p0"
2078 #ifdef HAVE_GNU_PATCH
2079 " -f -s"
2080 #endif
2081 " %s %s",
2082 src_data->file_name->str_text,
2083 patch_file_name.c_str()
2084 );
2085 int flags = OS_EXEC_FLAG_NO_INPUT; // + OS_EXEC_FLAG_SILENT;
2086 int n = os_execute_retcode(command, flags, dd);
2087 if (n == 0)
2088 {
2089 //
2090 // The patch applied cleanly.
2091 //
2092 need_whole_source = 0;
2093 }
2094 else
2095 {
2096 sub_context_ty sc;
2097 sc.var_set_string("File_Name", src_data->file_name);
2098 sc.error_intl(i18n("warning: $filename patch not used"));
2099 }
2100 break;
2101 }
2102 if (!patch_is_empty)
2103 os_unlink(patch_file_name);
2104
2105 //
2106 // The src file should be next.
2107 //
2108 archive_name.clear();
2109 ifp = cpio_p->child(archive_name);
2110 if (!ifp.is_open())
2111 {
2112 missing_file
2113 (
2114 cpio_p,
2115 nstring::format
2116 (
2117 "src/%s",
2118 old ? old->c_str() : src_data->file_name->str_text
2119 )
2120 );
2121 }
2122 assert(archive_name);
2123 }
2124
2125 // make sure we have the file we expected
2126 {
2127 nstring s1 =
2128 nstring::format("src/%s", src_data->file_name->str_text);
2129 nstring s2;
2130 if (old)
2131 s2 = "src/" + *old;
2132 if (archive_name != s1 && (!s2 || archive_name != s2))
2133 wrong_file(ifp, s1);
2134 }
2135
2136 output::pointer os;
2137 if (need_whole_source)
2138 os = output_file::binary_open(src_data->file_name);
2139 else
2140 os = output_bit_bucket::create();
2141 os << ifp;
2142 ifp.close();
2143 }
2144
2145 //
2146 // should be at end of input
2147 //
2148 archive_name.clear();
2149 ifp = cpio_p->child(archive_name);
2150 if (ifp.is_open())
2151 cpio_p->fatal_error("archive too long");
2152 delete cpio_p;
2153 cpio_p = 0;
2154 os_become_undo();
2155
2156 //
2157 // Now chmod the executable files,
2158 // and set the file attributes.
2159 //
2160 exec_mode = 0755 & ~cp->umask_get();
2161 non_exec_mode = exec_mode & ~0111;
2162 os_become_orig();
2163 for (j = 0; j < change_set->src->length; ++j)
2164 {
2165 cstate_src_ty *src_data;
2166
2167 src_data = change_set->src->list[j];
2168 if (src_data->attribute)
2169 {
2170 fattr_ty *fattr_data;
2171 string_ty *s2;
2172
2173 fattr_data = (fattr_ty *)fattr_type.alloc();
2174 fattr_data->attribute = attributes_list_copy(src_data->attribute);
2175 //
2176 // We remove the edit-origin-UUID from the file's
2177 // attributes list. It may be not correct for the local
2178 // repository.
2179 //
2180 attributes_list_remove(fattr_data->attribute, EDIT_ORIGIN_UUID);
2181 fattr_write_file(attribute_file_name, fattr_data, 0);
2182 fattr_type.free(fattr_data);
2183 s2 = str_quote_shell(src_data->file_name);
2184 nstring s =
2185 nstring::format
2186 (
2187 "aegis --file-attributes --change=%ld --project=%s "
2188 "--file=%s --verbose %s --base-rel%s",
2189 magic_zero_decode(change_number),
2190 project_name->str_text,
2191 attribute_file_name->str_text,
2192 s2->str_text,
2193 trace_options.c_str()
2194 );
2195 str_free(s2);
2196 os_execute(s, OS_EXEC_FLAG_INPUT, dd);
2197 os_unlink_errok(attribute_file_name);
2198 }
2199
2200 switch (src_data->action)
2201 {
2202 case file_action_create:
2203 case file_action_modify:
2204 break;
2205
2206 case file_action_remove:
2207 case file_action_insulate:
2208 case file_action_transparent:
2209 #ifndef DEBUG
2210 default:
2211 #endif
2212 continue;
2213 }
2214
2215 switch (src_data->usage)
2216 {
2217 case file_usage_source:
2218 case file_usage_config:
2219 case file_usage_test:
2220 case file_usage_manual_test:
2221 break;
2222
2223 case file_usage_build:
2224 continue;
2225 }
2226 assert(src_data->file_name);
2227
2228 os_chmod
2229 (
2230 src_data->file_name,
2231 (src_data->executable ? exec_mode : non_exec_mode)
2232 );
2233 }
2234 os_become_undo();
2235 str_free(attribute_file_name);
2236
2237 //
2238 // Now check to see if any of them were config files. We couldn't do
2239 // it before now, in case we got an inconsistent config combination.
2240 //
2241 config_seen = 0;
2242 for (j = 0; j < change_set->src->length; ++j)
2243 {
2244 cstate_src_ty *src_data;
2245
2246 src_data = change_set->src->list[j];
2247 switch (src_data->action)
2248 {
2249 case file_action_create:
2250 case file_action_modify:
2251 break;
2252
2253 case file_action_remove:
2254 case file_action_insulate:
2255 case file_action_transparent:
2256 #ifndef DEBUG
2257 default:
2258 #endif
2259 continue;
2260 }
2261 switch (src_data->usage)
2262 {
2263 case file_usage_build:
2264 continue;
2265
2266 case file_usage_source:
2267 assert(src_data->file_name);
2268 if (cp->file_is_config(src_data->file_name))
2269 {
2270 could_have_a_trojan = 1;
2271 config_seen = 1;
2272 }
2273 break;
2274
2275 case file_usage_config:
2276 could_have_a_trojan = 1;
2277 config_seen = 1;
2278 break;
2279
2280 case file_usage_test:
2281 case file_usage_manual_test:
2282 could_have_a_trojan = 1;
2283 break;
2284 }
2285 }
2286 change_free(cp);
2287 cp = 0;
2288
2289 //
2290 // If the change could have a trojan horse in it, stop here with
2291 // a warning. The user needs to look at it and check.
2292 //
2293 if (trojan > 0)
2294 could_have_a_trojan = 1;
2295 else if (trojan == 0)
2296 {
2297 error_intl(0, i18n("warning: potential trojan, proceeding anyway"));
2298 could_have_a_trojan = 0;
2299 config_seen = 0;
2300 }
2301
2302 //
2303 // If the change could have a trojan horse in the project config
2304 // file, stop here with a warning. Don't even difference the
2305 // change, because the trojan could be embedded in the diff
2306 // command. The user needs to look at it and check.
2307 //
2308 // FIX ME: what if the aecpu got rid of it?
2309 //
2310 if (config_seen)
2311 {
2312 //
2313 // before we bail, see if we can throw everything away.
2314 //
2315 if (ignore_uuid <= 0)
2316 {
2317 change::pointer other_cp = local_inventory.query(incoming_uuids);
2318 if (other_cp)
2319 {
2320 cp = change_alloc(pp, change_number);
2321 change_bind_existing(cp);
2322 if (nothing_has_changed(cp, up))
2323 {
2324 nstring attr =
2325 nstring::format
2326 (
2327 "%s+=%s",
2328 ORIGINAL_UUID,
2329 change_set->uuid->str_text
2330 );
2331 nstring s2 =
2332 nstring::format
2333 (
2334 "aegis -change-attr %s -change=%ld -project=%s%s",
2335 attr.quote_shell().c_str(),
2336 magic_zero_decode(other_cp->number),
2337 project_name_get(other_cp->pp).c_str(),
2338 trace_options.c_str()
2339 );
2340 os_become_orig();
2341 os_execute(s2, OS_EXEC_FLAG_INPUT | OS_EXEC_FLAG_ERROK, dd);
2342 os_become_undo();
2343
2344 goto cancel_the_change;
2345 }
2346 change_free(cp);
2347 cp = 0;
2348 }
2349 }
2350
2351 error_intl
2352 (
2353 0,
2354 i18n("warning: potential trojan, review before completing development")
2355 );
2356
2357 //
2358 // We set now the uuid of the change so it is preserved.
2359 //
2360 change_uuid_set
2361 (
2362 nstring(project_name),
2363 change_number,
2364 nstring(change_set->uuid),
2365 trace_options,
2366 dd
2367 );
2368
2369 //
2370 // Make sure we are using an appropriate architecture. This is
2371 // one of the commonest problems when seeding an empty repository.
2372 //
2373 nstring s =
2374 nstring::format
2375 (
2376 "aegis --change-attr --fix-arch --change=%ld --project=%s%s",
2377 magic_zero_decode(change_number),
2378 project_name->str_text,
2379 trace_options.c_str()
2380 );
2381 os_become_orig();
2382 os_execute(s, OS_EXEC_FLAG_INPUT, dd);
2383 os_become_undo();
2384 return;
2385 }
2386
2387 //
2388 // now merge the change
2389 //
2390 {
2391 nstring merge_command =
2392 nstring::format
2393 (
2394 "aegis --diff --only-merge --change=%ld --project=%s --verbose",
2395 magic_zero_decode(change_number),
2396 project_name->str_text
2397 );
2398 os_become_orig();
2399 os_execute(merge_command, OS_EXEC_FLAG_INPUT, dd);
2400 os_become_undo();
2401 }
2402
2403 //
2404 // Now that all the files have been unpacked,
2405 // set the change's UUID.
2406 //
2407 // It is vaguely possible you have already downloaded this change
2408 // before, so we don't complain (OS_EXEC_FLAG_ERROK) if the command
2409 // fails.
2410 //
2411 // We intentionally set the UUID before the aecpu -unch. The aecpu
2412 // -unch will clear the UUID if the change's file inventory changes,
2413 // because it is no longer the same change set.
2414 //
2415 if (change_set->uuid)
2416 {
2417 change_uuid_set
2418 (
2419 nstring(project_name),
2420 change_number,
2421 nstring(change_set->uuid),
2422 trace_options,
2423 dd
2424 );
2425 }
2426
2427 //
2428 // If we are receiving a change set with an UUID, and the user has
2429 // not used the --ignore-uuid option, we avoid the uncopy files so
2430 // the UUID is preserved and we don't need a new one. This should
2431 // reduce the UUID proliferation and some related side effects.
2432 //
2433 if (change_set->uuid && ignore_uuid <= 0)
2434 uncopy = 0;
2435
2436 //
2437 // Un-copy any files which did not change.
2438 //
2439 // The idea is, if there are no files left, there is nothing
2440 // for this change to do, so cancel it.
2441 //
2442 // We intentionally set the UUID before the aecpu -unch. The aecpu
2443 // -unch will clear the UUID if the change's file inventory changes,
2444 // because it is no longer the same change set.
2445 //
2446 if (uncopy)
2447 {
2448 {
2449 nstring copy_file_undo_cmd =
2450 nstring::format
2451 (
2452 "aegis --copy-file-undo --unchanged --change=%ld "
2453 "--project=%s --verbose%s",
2454 magic_zero_decode(change_number),
2455 project_name->str_text,
2456 trace_options.c_str()
2457 );
2458 os_become_orig();
2459 os_execute(copy_file_undo_cmd, OS_EXEC_FLAG_INPUT, dd);
2460 os_become_undo();
2461 }
2462
2463 //
2464 // If there are no files left, we already have this change.
2465 //
2466 if (number_of_files(project_name, change_number) == 0)
2467 {
2468 cancel_the_change:
2469
2470 //
2471 // get out of there
2472 //
2473 os_chdir(dot);
2474
2475 //
2476 // stop developing the change
2477 // cancel the change
2478 //
2479 nstring s =
2480 nstring::format
2481 (
2482 "aegis --develop-begin-undo --change=%ld --project=%s "
2483 "--verbose%s --new-change-undo",
2484 magic_zero_decode(change_number),
2485 project_name->str_text,
2486 trace_options.c_str()
2487 );
2488 os_become_orig();
2489 os_execute(s, OS_EXEC_FLAG_INPUT, dot);
2490
2491 //
2492 // run away, run away!
2493 //
2494 error_intl(0, i18n("change already present"));
2495 return;
2496 }
2497 }
2498 else if (ignore_uuid <= 0)
2499 {
2500 //
2501 // If this change set actually changes nothing (all the file
2502 // contents are already the same as in the project) and the
2503 // change set UUID is already in the project, then get rid of
2504 // this change set, and add the UUID to the existing change.
2505 //
2506 change::pointer other_cp = local_inventory.query(incoming_uuids);
2507 if (other_cp)
2508 {
2509 cp = change_alloc(pp, change_number);
2510 change_bind_existing(cp);
2511 if (nothing_has_changed(cp, up))
2512 {
2513 nstring attr =
2514 nstring::format
2515 (
2516 "%s+=%s",
2517 ORIGINAL_UUID,
2518 change_set->uuid->str_text
2519 );
2520 nstring s2 =
2521 nstring::format
2522 (
2523 "aegis -change-attr %s -change=%ld -project=%s%s",
2524 attr.quote_shell().c_str(),
2525 magic_zero_decode(other_cp->number),
2526 project_name_get(other_cp->pp).c_str(),
2527 trace_options.c_str()
2528 );
2529 os_become_orig();
2530 os_execute(s2, OS_EXEC_FLAG_INPUT | OS_EXEC_FLAG_ERROK, dd);
2531 os_become_undo();
2532
2533 goto cancel_the_change;
2534 }
2535 change_free(cp);
2536 cp = 0;
2537 }
2538 }
2539
2540 //
2541 // now diff the change
2542 //
2543 {
2544 nstring diff_command =
2545 nstring::format
2546 (
2547 "aegis --diff --no-merge --change=%ld --project=%s --verbose%s",
2548 magic_zero_decode(change_number),
2549 project_name->str_text,
2550 trace_options.c_str()
2551 );
2552 os_become_orig();
2553 os_execute(diff_command, OS_EXEC_FLAG_INPUT, dd);
2554 os_become_undo();
2555 }
2556
2557 //
2558 // If the change could have a trojan horse in it, stop here with
2559 // a warning. The user needs to look at it and check.
2560 //
2561 if (could_have_a_trojan)
2562 {
2563 error_intl
2564 (
2565 0,
2566 i18n("warning: potential trojan, review before completing development")
2567 );
2568 return;
2569 }
2570
2571 //
2572 // Sleep for a second to make sure the derived files will have
2573 // mod-times strictly later than the source files, and that the aeb
2574 // timestamp will also be strictly later then the mod times for the
2575 // source files.
2576 //
2577 sleep(1);
2578
2579 //
2580 // now build the change
2581 //
2582 {
2583 nstring build_command =
2584 nstring::format
2585 (
2586 "aegis --build --change=%ld --project=%s --verbose%s",
2587 magic_zero_decode(change_number),
2588 project_name->str_text,
2589 trace_options.c_str()
2590 );
2591 os_become_orig();
2592 os_execute(build_command, OS_EXEC_FLAG_INPUT, dd);
2593 os_become_undo();
2594 }
2595
2596 //
2597 // Sleep for a second to make sure the aet timestamps will be
2598 // strictly later then the aeb timestamp.
2599 //
2600 sleep(1);
2601
2602 //
2603 // re-read the change state data.
2604 //
2605 cp = change_alloc(pp, change_number);
2606 change_bind_existing(cp);
2607 cstate_ty *cstate_data = cp->cstate_get();
2608
2609 //
2610 // now test the change
2611 //
2612 if (need_to_test && !cstate_data->test_exempt)
2613 {
2614 nstring s =
2615 nstring::format
2616 (
2617 "aegis --test --change=%ld --project=%s --verbose%s",
2618 magic_zero_decode(change_number),
2619 project_name->str_text,
2620 trace_options.c_str()
2621 );
2622 os_become_orig();
2623 os_execute(s, OS_EXEC_FLAG_INPUT, dd);
2624 os_become_undo();
2625 }
2626 if (need_to_test && !cstate_data->test_baseline_exempt)
2627 {
2628 nstring s =
2629 nstring::format
2630 (
2631 "aegis --test --baseline --change=%ld --project=%s "
2632 "--verbose%s",
2633 magic_zero_decode(change_number),
2634 project_name->str_text,
2635 trace_options.c_str()
2636 );
2637 os_become_orig();
2638 os_execute(s, OS_EXEC_FLAG_INPUT, dd);
2639 os_become_undo();
2640 }
2641
2642 // always to a regession test?
2643 if (!cstate_data->regression_test_exempt)
2644 {
2645 nstring s =
2646 nstring::format
2647 (
2648 "aegis --test --regression --change=%ld --project=%s "
2649 "--verbose%s",
2650 magic_zero_decode(change_number),
2651 project_name->str_text,
2652 trace_options.c_str()
2653 );
2654 os_become_orig();
2655 os_execute(s, OS_EXEC_FLAG_INPUT, dd);
2656 os_become_undo();
2657 }
2658
2659 change_free(cp);
2660 cp = 0;
2661 project_free(pp);
2662 pp = 0;
2663
2664 //
2665 // end development (if we got this far!)
2666 //
2667 nstring s =
2668 nstring::format
2669 (
2670 "aegis --develop-end --change=%ld --project=%s --verbose%s",
2671 magic_zero_decode(change_number),
2672 project_name->str_text,
2673 trace_options.c_str()
2674 );
2675 os_become_orig();
2676 os_execute(s, OS_EXEC_FLAG_INPUT, dd);
2677 os_become_undo();
2678
2679 // verbose success message here?
2680 }
2681
2682
2683 // vim: set ts=8 sw=4 et :
2684