// // aegis - project change supervisor // Copyright (C) 1999, 2001-2008, 2011, 2012, 2014 Peter Miller // Copyright (C) 2007-2009 Walter Franzini // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 3 of the License, or (at // your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, see . // #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include struct slink_info_ty { string_list_ty stack; change::pointer cp; pconf_ty *pconf_data; user_ty::pointer up; const work_area_style_ty *style; int umask; ~slink_info_ty() { } slink_info_ty() : pconf_data(0), style(0), umask(022) { } }; static string_ty *dot; static symtab *derived_symlinks; enum file_status { file_status_not_related, file_status_up_to_date, file_status_out_of_date }; static file_status check_symbolic_link_to_baseline(string_ty *path_rel, string_list_ty *stack, int start) { // // We know it's a symbolic link. // We know it's in the development directory. // nstring path_abs(os_path_cat(stack->front(), path_rel)); nstring dest_abs(os_readlink(path_abs.get_ref())); if (dest_abs[0] != '/') { // // It's relative, so Aegis didn't create it. Leave it alone. // return file_status_not_related; } nstring dest_rel(dir_stack_relative(stack, dest_abs.get_ref())); if (dest_rel.empty()) { // // It doesn't point into the directory stack, so Aegis didn't // create it. Leave it alone. // return file_status_not_related; } if (dest_rel != nstring(path_rel)) { // // It points into the directory stack, bit it doesn't point at // the corresponding file, so Aegis didn't create it. Leave it // alone. // return file_status_not_related; } // // The symlink points to *a* corresponding file. See if it points // to the *correct* level of the directory stack. // nstring supposed_to_be(dir_stack_find(stack, start, path_rel, 0, 0, 1)); if (supposed_to_be != dest_abs) return file_status_out_of_date; return file_status_up_to_date; } static bool stat_same_file(const struct stat &st1, const struct stat &st2) { return (st1.st_dev == st2.st_dev && st1.st_ino == st2.st_ino); } static file_status check_hard_link_to_baseline(const nstring &, const struct stat &dst_st, const nstring &, const struct stat &src_st) { // // We know it's a regular file. // We know it's in the development directory. // // // If it isn't a hard link, it isn't related. // if (!S_ISREG(src_st.st_mode) || !S_ISREG(dst_st.st_mode)) return file_status_not_related; // // See that the correct file is, and see if the link goes to the // correct file. // if (stat_same_file(src_st, dst_st)) return file_status_up_to_date; return file_status_out_of_date; } static file_status check_copy_of_baseline(const nstring &dst_abs, const struct stat &dst_st, const nstring &src_abs, const struct stat &src_st) { // // Make sure the files are identical. // if ( dst_st.st_size != src_st.st_size || files_are_different(dst_abs.get_ref(), src_abs.get_ref()) ) return file_status_out_of_date; // // Looks good. // return file_status_up_to_date; } static bool try_to_make_hard_link_to_baseline(const nstring &dst, const nstring &src) { os_become_must_be_active(); int err = glue_link(src.c_str(), dst.c_str()); if (err == 0) return true; int errno_old = errno; switch (errno_old) { case EXDEV: // // Cross device (file system) hard link. // return false; case ENOSYS: #ifdef EOPNOTSUPP case EOPNOTSUPP: #endif // // This is a guess. The only scenario this will happen for is a // system which has hard links, using a network file system to a // server which does NOT have hard links. // return false; } sub_context_ty *scp = sub_context_new(); sub_errno_setx(scp, errno_old); sub_var_set_string(scp, "File_Name1", src.get_ref()); sub_var_set_string(scp, "File_Name2", dst.get_ref()); fatal_intl(scp, i18n("link(\"$filename1\", \"$filename2\"): $errno")); // NOTREACHED return false; } static bool try_to_make_symbolic_link_to_baseline(const nstring &dst, const nstring &src) { #ifdef S_IFLNK os_become_must_be_active(); int err = glue_symlink(src.c_str(), dst.c_str()); if (err == 0) { #ifdef HAVE_LUTIME // // If possible, set the mtime of the symlink. It appears that // GNU make looks at symlink mod times when figuring out what to // build. Sigh. // // Ignore any error returned, it doesn't matter if it can't be // set correctly. // struct utimes utb; utb.actime = os_mtime_actual(src.get_ref()); utb.modtime = utb.actime; glue_lutime(&utb, dst.c_str()); #endif return true; } int errno_old = errno; switch (errno_old) { case EPERM: // The filesystem containing the destination does not support // the creation of symbolic links. case ENOSYS: // This operating doesn't grok symlinks. #ifdef EOPNOTSUPP case EOPNOTSUPP: // This operating doesn't grok symlinks. #endif // // This is a guess. The only scenario this will happen for is a // system which has symlinks, using a network file system to a // server which does NOT have symbolic links. // return false; } sub_context_ty *scp = sub_context_new(); sub_errno_setx(scp, errno_old); sub_var_set_string(scp, "File_Name1", src.get_ref()); sub_var_set_string(scp, "File_Name2", dst.get_ref()); fatal_intl(scp, i18n("symlink(\"$filename1\", \"$filename2\"): $errno")); // NOTREACHED #endif return false; } static bool comma_d(const nstring &s) { return (s.size() > 2 && s[s.size() - 2] == ',' && s[s.size() - 1] == 'D'); } static bool is_a_symlink_exception(const nstring &path, slink_info_ty *sip) { pconf_symlink_exceptions_list_ty *lp = sip->pconf_data->symlink_exceptions; assert(lp); if (!lp) return false; for (size_t j = 0; j < lp->length; ++j) { if (path.gmatch(lp->list[j]->str_text)) return true; } return false; } static void os_symlink_repair(string_ty *value, string_ty *filename) { string_ty *s; // // Most of the time, this results in one system call, because the // symlink already exists. // // Only rarely will it result in three system calls. // // The case for creating new links takes a different code path, and // never enters this function. It, too, only uses one system call. // s = os_readlink(filename); trace(("readlink \"%s\" -> \"%s\"\n", filename->str_text, s->str_text)); if (!str_equal(s, value)) { trace(("rm \"%s\"\n", filename->str_text)); os_unlink(filename); trace(("ln -s \"%s\" \"%s\"\n", value->str_text, filename->str_text)); os_symlink(value, filename); } str_free(s); } static void os_lstat(const nstring &path, struct stat &st) { os_become_must_be_active(); #ifdef S_IFLNK int oret = glue_lstat(path.c_str(), &st); #else int oret = glue_stat(path.c_str(), &st); #endif if (oret) { int errno_old = errno; sub_context_ty sc; sc.errno_setx(errno_old); sc.var_set_string("File_Name", path); sc.fatal_intl(i18n("stat $filename: $errno")); // NOTREACHED } } static bool is_an_aegis_symlink(string_ty *path_rel, string_list_ty *stack, int start) { trace(("is_an_aegis_symlink(\"%s\", stack, %d)\n{\n", path_rel->str_text, start)); nstring path_abs(os_path_cat((*stack)[start], path_rel)); nstring dest_abs(os_readlink(path_abs.get_ref())); if (dest_abs[0] != '/') { // // It's relative, so Aegis didn't create it. Leave it alone. // trace(("return false; /* relative path */\n}\n")); return false; } if (os_isa_special_file(dest_abs.get_ref())) { // // The destination file is not a regular file, so Aegis didn't // create it. // trace(("return false; /* special file */\n}\n")); return false; } for (size_t c = start; c < stack->size(); ++c) { nstring test_path(os_path_cat((*stack)[c], path_rel)); if (str_equal(test_path, dest_abs)) { // // We have found a match so this test pass. // break; } if (c == stack->size()-1) { // // The destination file is not into the stack, so Aegis // did't create it. // trace(("return false; /* not into the stack */\n}\n")); return false; } } trace(("return true;\n")); trace(("}\n")); return true; } // // NAME // maintain // // SYNOPSIS // void maintain(void *arg, dir_stack_walk_message_t msg, string_ty *path, // struct stat *st, int depth); // // DESCRIPTION // The maintain function is used to maintain symbolic links in the // development directory. It is called once for each file by the // dir_stack_walk function. // // arg The argument passed to dir_stack_walk // msg The message indicating what the file is // path The RELATIVE path name of the file, relative to the // directory stack. // st The stat structure describing the file. // depth The depth down the directory stack. Zero means in the // development directory (or integration directory). // static void maintain(void *p, dir_stack_walk_message_t msg, string_ty *path_rel, struct stat *st, int depth, int) { trace(("maintain(path_rel = \"%s\", depth = %d)\n{\n", path_rel->str_text, depth & ~TOP_LEVEL_SYMLINK)); bool top_level_symlink = !!(depth & TOP_LEVEL_SYMLINK); trace(("top_level_symlink = %d\n", top_level_symlink)); depth &= ~TOP_LEVEL_SYMLINK; slink_info_ty *sip = (slink_info_ty *)p; nstring path_abs(os_path_cat(sip->stack.front(), path_rel)); change::pointer cp = sip->cp; sip->up->become_end(); fstate_src_ty *c_src = cp->file_find(nstring(path_rel), view_path_first); project *pp = cp->project_get(); project *ppp = (pp->is_a_trunk() ? 0 : pp->parent_get()); if (cp->is_being_integrated()) { if (!c_src) { c_src = pp->change_get()->file_find(nstring(path_rel), view_path_first); } pp = ppp; } fstate_src_ty *p_src = (pp ? pp->file_find(path_rel, view_path_simple) : 0); sip->up->become_begin(); switch (msg) { case dir_stack_walk_dir_before: // // We don't do anything here, because we want to create as few // directories as possible. See the os_mkdir_between calls, below. // trace(("dir before\n")); break; case dir_stack_walk_dir_after: trace(("dir after\n")); break; case dir_stack_walk_symlink: trace(("symlink\n")); // // The dir_stack_walk function was told to ignore symlinks, // so if it returns a symlink it is a derived file, or a // symlink maintained by this function in a deeper branch. // if ( !sip->style->derived_file_link && !sip->style->derived_file_symlink && !sip->style->derived_file_copy ) break; if (top_level_symlink) break; if (!is_an_aegis_symlink(path_rel, &sip->stack, depth)) { nstring src_abs(os_path_cat(sip->stack[depth], path_rel)); nstring path_target(os_readlink(src_abs.get_ref())); if (!os_exists(path_abs.get_ref())) { os_mkdir_between(sip->stack.front(), path_rel, 02755); os_symlink(path_target.get_ref(), path_abs.get_ref()); } if (sip->style->during_build_only) { assert(derived_symlinks); derived_symlinks->assign(nstring(path_rel), path_target); } } break; case dir_stack_walk_special: // // This can't be a source file, it has to be a derived file. // Let them build it again if it isn't in the top level // directory; we ignore it. // trace(("special\n")); break; case dir_stack_walk_file: trace(("dir_stack_walk_file\n")); bool file_restored = false; if ( ( sip->style->derived_file_link || sip->style->derived_file_symlink || sip->style->derived_file_copy ) && p_src && (p_src->action == file_action_remove) ) { // // Ralph Smith: If the visible file is shallower than the // removal, then it has presumably been restored as a // derived file. If it is stale, the user gets to deal with // it. // // Can't use project_file_path() for a removed file. // Is there a simpler way to do this? // int rem_depth = 1; project *ptmp; fstate_src_ty *tmp_src; for ( ptmp = pp->is_a_trunk() ? 0 : pp->parent_get(); ptmp; ptmp = (ptmp->is_a_trunk() ? 0 : ptmp->parent_get()) ) { sip->up->become_end(); tmp_src = ptmp->file_find(path_rel, view_path_simple); sip->up->become_begin(); if (tmp_src && tmp_src->action == file_action_remove) rem_depth++; else break; } trace(("rem_depth: %d\n",rem_depth)); if (rem_depth >= depth) file_restored = true; } if (top_level_symlink) { trace(("top level symlink\n")); if (file_restored) { trace(("symlink to a restored derived file")); goto done; } if (c_src) { switch (c_src->action) { case file_action_remove: trace(("file_action_remove\n")); trace(("rm %s\n", path_abs.c_str())); os_unlink(path_abs.get_ref()); break; case file_action_transparent: { trace(("transparent\n")); if (!ppp) { trace(("rm %s\n", path_abs.c_str())); os_unlink(path_abs.get_ref()); break; } // // Note: we don't use dir_stack_find, there could be // stale files too early in the stack. // sip->up->become_end(); nstring origin(project_file_path(ppp, path_rel)); sip->up->become_begin(); if (origin.empty()) { trace(("rm %s\n", path_abs.c_str())); os_unlink(path_abs.get_ref()); break; } os_mkdir_between(sip->stack.front(), path_rel, 02755); os_symlink_repair(origin.get_ref(), path_abs.get_ref()); } break; case file_action_create: case file_action_modify: case file_action_insulate: // // This symlink should not be here, but where the // heck did the real file go? Not having a good // answer, we do nothing. // break; } goto done; } if (p_src) { // // Note: we don't use dir_stack_find, there could be // stale files too early in the stack. // trace(("project file\n")); if (p_src->action == file_action_remove) { trace(("rm %s\n", path_abs.c_str())); os_unlink(path_abs.get_ref()); } else { switch(p_src->usage) { case file_usage_build: // // Do not repair links related to build files. // They should follow the derived_files_* style. // break; case file_usage_config: case file_usage_source: case file_usage_manual_test: case file_usage_test: #ifndef DEBUG default: #endif { sip->up->become_end(); nstring origin(project_file_path(pp, path_rel)); sip->up->become_begin(); assert(!origin.empty()); os_mkdir_between ( sip->stack.front(), path_rel, 02755 ); os_symlink_repair ( origin.get_ref(), path_abs.get_ref() ); } break; } } goto done; } trace(("check symbolic link to baseline\n")); switch (check_symbolic_link_to_baseline(path_rel, &sip->stack, 1)) { case file_status_not_related: case file_status_up_to_date: goto done; case file_status_out_of_date: // // Do not unlink derived file registered into aegis. // if (p_src && p_src->usage == file_usage_build) goto done; trace(("rm %s\n", path_abs.c_str())); os_unlink(path_abs.get_ref()); depth = 666; break; } } if (depth == 0) { // // Note: there is no os_mkdir_between call required, because // the file exists at the top level (in the development // directory or integration directory). // trace(("file at top\n")); if (c_src) { trace(("change file\n")); switch (c_src->action) { case file_action_remove: // This is probably whiteout. // Leave it alone. break; case file_action_transparent: // // Note: we don't use dir_stack_find, there could be // stale files too early in the stack. // p_src = 0; if (ppp) { sip->up->become_end(); p_src = ppp->file_find(path_rel, view_path_extreme); sip->up->become_begin(); } if (!p_src) { trace(("rm %s\n", path_abs.c_str())); os_unlink(path_abs.get_ref()); } break; case file_action_create: case file_action_modify: case file_action_insulate: break; } goto done; } if (p_src) { trace(("project file at top\n")); if (p_src->action == file_action_remove) { // This is probably whiteout. // Leave it alone. goto done; } // // Note: we don't use dir_stack_find, there could be // stale files too early in the stack. // sip->up->become_end(); nstring origin(project_file_path(pp, path_rel)); sip->up->become_begin(); assert(!origin.empty()); struct stat st1; os_lstat(origin, st1); // // If the file is an accurate reflection of the // baseline, we do not need to do anything else. // if ( ( check_hard_link_to_baseline(path_abs, *st, origin, st1) == file_status_up_to_date ) || ( check_copy_of_baseline(path_abs, *st, origin, st1) == file_status_up_to_date ) ) goto done; // // If the file is a registered derived file then we do // not do anything else. // if (p_src->usage == file_usage_build) goto done; // // Not an accurate reflection of the baseline, // get rid of it and start again. // trace(("rm %s\n", path_abs.c_str())); os_unlink(path_abs.get_ref()); if ( ( !sip->style->source_file_link || !try_to_make_hard_link_to_baseline(path_abs, origin) ) && ( !sip->style->source_file_symlink || !try_to_make_symbolic_link_to_baseline(path_abs, origin) ) && sip->style->source_file_copy ) { // // This is a bit subtle. When aecpu or aemtu call // change_maintain_symlinks_to_baseline they tell // it that they are undoing, which in turn causes // the style to be altered to copy source files and // prevents hard links or symbolic links. The aecpu // and aemtu commands DO NOT want to copy mod time // (cmt) of source files in the baseline, but they // do want a copy of the file contents to attach // the alternate mod time to. // bool cmt = ( !sip->style->source_file_copy && !sip->style->derived_file_copy ); copy_whole_file(origin.get_ref(), path_abs.get_ref(), cmt); int file_mode = 0444; if (p_src->executable) file_mode |= 0111; os_chmod(path_abs.get_ref(), file_mode & ~sip->umask); } goto done; } trace(("derived file\n")); // // It isn't possible to distinguish a copy which is out // of date because the file in the baseline changed from // a derived file in the development directory. // // It isn't possible to distinguish a hard link which is // out of date because the file in the baseline changed // (and thus broke the link) from a derived file in the // development directory. // // In each case we do nothing, and trust that the build will // bring it back up-to-date. // goto done; } trace(("file NOT at top\n")); assert(depth > 0); if (c_src) { trace(("change file not at top?\n")); switch (c_src->action) { case file_action_create: case file_action_modify: case file_action_insulate: // // This source file is supposed to be here, but where // the heck did the real file go? Not having a good // answer, we do nothing. // break; case file_action_remove: break; case file_action_transparent: // // This file should be a copy of the grandparent (or // deeper) file, or a hard link to the file, but it // isn't there. // if (!ppp) break; // // Note: we don't use dir_stack_find, there could be // stale files too early in the stack. // sip->up->become_end(); nstring origin(project_file_path(ppp, path_rel)); fstate_src_ty *ppp_src = ppp->file_find(path_rel, view_path_extreme); assert(ppp_src); sip->up->become_begin(); if (origin.empty()) break; // // Do not process derived files registered within Aegis. // if (c_src->usage == file_usage_build) break; os_mkdir_between(sip->stack.front(), path_rel, 02755); if ( !try_to_make_hard_link_to_baseline(path_abs, origin) && !try_to_make_symbolic_link_to_baseline(path_abs, origin) ) { copy_whole_file(origin.get_ref(), path_abs.get_ref(), 0); int file_mode = 0444; if (ppp_src->executable) file_mode |= 0111; os_chmod(path_abs.get_ref(), file_mode & ~sip->umask); } break; } goto done; } if (p_src && !file_restored) { // // There is no file in the development directory, there is // no change file, but there is a project source file. // trace(("project file not at top\n")); if (p_src->action == file_action_remove) break; if (p_src->usage == file_usage_build) break; os_mkdir_between(sip->stack.front(), path_rel, 02755); // // Note: we don't use dir_stack_find, there could be // stale files too early in the stack. // sip->up->become_end(); nstring origin(project_file_path(pp, path_rel)); sip->up->become_begin(); assert(!origin.empty()); if ( ( !sip->style->source_file_link || !try_to_make_hard_link_to_baseline(path_abs, origin) ) && ( !sip->style->source_file_symlink || !try_to_make_symbolic_link_to_baseline(path_abs, origin) ) && sip->style->source_file_copy ) { copy_whole_file(origin.get_ref(), path_abs.get_ref(), 1); int file_mode = 0444; if (p_src->executable) file_mode |= 0111; os_chmod(path_abs.get_ref(), file_mode & ~sip->umask); } } else if ( sip->cp->is_being_developed() && comma_d(nstring(path_rel)) ) { // // Do not make links or symlinks or copies of the difference // files produced by aed. They just make the work area // busier for no good reason (and stop aedless from working). // } else if (!is_a_symlink_exception(nstring(path_rel), sip)) { // // There is no file in the development directory, there is // no change file and there is no project file. Therefore, // this is a derived file in the baseline, created by an // integration build. // // Make a link or copy, if we have been asked to, and if // this file isn't one of the exceptions. The name "symlink // exceptions" reveals the history of this functionality, // since generalised to cover hard links and cipies as well. // trace(("derived file not at top\n")); os_mkdir_between(sip->stack.front(), path_rel, 02755); nstring origin(os_path_cat(sip->stack[depth], path_rel)); if ( ( !sip->style->derived_file_link || !try_to_make_hard_link_to_baseline(path_abs, origin) ) && ( !sip->style->derived_file_symlink || !try_to_make_symbolic_link_to_baseline(path_abs, origin) ) && sip->style->derived_file_copy ) { copy_whole_file(origin.get_ref(), path_abs.get_ref(), 1); int file_mode = 0644; if (os_executable(origin.get_ref())) file_mode |= 0111; os_chmod(path_abs.get_ref(), file_mode & ~sip->umask); } } break; } done: trace(("}\n")); } // // NAME // change_create_symlinks_to_baseline // // SYNOPSIS // void change_create_symlinks_to_baseline(change::pointer cp, // project *pp, user_ty::pointer up, work_area_style_ty *style); // // DESCRIPTION // The change_create_symlinks_to_baseline function is used to create // symbolic links between a development directory (or integration // directory) and the baseline (and ancestor baselines). It does // this in two passes, the first creates the links, and the second // removes dead links. // void change_create_symlinks_to_baseline(const change::pointer &cp, user_ty::pointer up, const work_area_style_ty &style) { slink_info_ty si; trace(("change_create_symlinks_to_baseline(cp = %p)\n{\n", cp.get())); trace(("source_file_link = %d\n", style.source_file_link)); trace(("source_file_symlink = %d\n", style.source_file_symlink)); trace(("source_file_copy = %d\n", style.source_file_copy)); trace(("derived_file_link = %d\n", style.derived_file_link)); trace(("derived_file_symlink = %d\n", style.derived_file_symlink)); trace(("derived_file_copy = %d\n", style.derived_file_copy)); if ( !style.source_file_link && !style.source_file_symlink && !style.source_file_copy && !style.derived_file_link && !style.derived_file_symlink && !style.derived_file_copy ) { trace(("}\n")); return; } if (style.during_build_only) derived_symlinks = new symtab; trace(("during_build_only = %d\n", style.during_build_only)); trace(("derived_at_start_only = %d\n", style.derived_at_start_only)); assert(cp->valid()); change_verbose(cp, 0, i18n("creating symbolic links to baseline")); // // Work out the search path. // cp->search_path_get(&si.stack, false); #ifdef DEBUG { size_t k; for (k = 0; k < si.stack.size(); ++k) trace(("si.stack[%ld] = \"%s\"\n", (long)k, si.stack[k]->str_text)); } #endif // // For each ancestor, create symlinks from the development // directory to that ancestor's baseline if the file does not // already exist. // si.cp = cp; si.pconf_data = change_pconf_get(cp, 0); si.style = &style; si.up = up; si.umask = cp->umask_get(); if (!dot) dot = str_from_c("."); up->become_begin(); dir_stack_walk(&si.stack, dot, maintain, &si, 1); up->become_end(); trace(("}\n")); } static void unmaintain(void *p, dir_stack_walk_message_t msg, string_ty *path, struct stat *, int depth, int) { trace(("unmaintain(path = \"%s\", msg = %d, depth = %d)\n{\n", path->str_text, msg, depth & ~TOP_LEVEL_SYMLINK)); bool top_level_symlink = !!(depth & TOP_LEVEL_SYMLINK); depth &= ~TOP_LEVEL_SYMLINK; if (depth && !top_level_symlink) { trace(("}\n")); return; } slink_info_ty *sip = (slink_info_ty *)p; trace(("mark\n")); assert(sip->style->during_build_only); switch (msg) { case dir_stack_walk_dir_before: case dir_stack_walk_dir_after: trace(("is a directory\n")); break; case dir_stack_walk_special: break; case dir_stack_walk_symlink: trace(("is a symlink\n")); if ( top_level_symlink || !is_an_aegis_symlink(path, &sip->stack, depth) ) { // // Remember: the top-level-symlink flags means that the // development directory contains a symlink over the top of // some other type of file. // trace(("mark\n")); nstring path_abs(os_path_cat(sip->stack.front(), path)); nstring dest(os_readlink(path_abs.get_ref())); nstring dest_rel(dir_stack_relative(&sip->stack, dest.get_ref())); // // We keep track of derived symlinks Aegis create in the // working directory and, after the build, remove those // not modified. // if (is_an_aegis_symlink(path, &sip->stack, depth)) { if (dest_rel.empty() || nstring(path) != dest_rel) break; } else { assert(derived_symlinks); nstring *old_path = derived_symlinks->query(path); if (old_path == NULL || old_path->empty()) break; if (*old_path != dest) break; } trace(("rm %s\n", path_abs.c_str())); os_unlink(path_abs.get_ref()); } break; case dir_stack_walk_file: trace(("is a file\n")); { // scope for c_src: sip->up->become_end(); fstate_src_ty *c_src = sip->cp->file_find(nstring(path), view_path_first); sip->up->become_begin(); if (c_src) break; } trace(("mark\n")); if (top_level_symlink) { // // Remove symlinks in the development directory which point // to a counterpart in the baseline (any counterpart, not // necessarily the right one). // trace(("mark\n")); nstring path_abs(os_path_cat(sip->stack.front(), path)); nstring dest(os_readlink(path_abs.get_ref())); nstring dest_rel(dir_stack_relative(&sip->stack, dest.get_ref())); if (!dest_rel.empty() && nstring(path) == dest_rel) { trace(("rm %s\n", path_abs.c_str())); os_unlink(path_abs.get_ref()); } } else { // // Remove hard links in the development directory which // have a counterpart in the baseline (any counterpart, not // necessarily the right one, and the link could have been // broken). // // Watch out for integration builds, which need to check for // grandparent files BUT not nuke project files from this branch. // trace(("mark\n")); project *pp = sip->cp->project_get(); sip->up->become_end(); if (sip->cp->is_being_integrated()) { change::pointer branch_cp = sip->cp->project_get()->change_get(); if (branch_cp->file_find(nstring(path), view_path_first)) { sip->up->become_begin(); break; } if (pp->is_a_trunk()) { sip->up->become_begin(); break; } pp = pp->parent_get(); } fstate_src_ty *p_src = pp->file_find(path, view_path_simple); sip->up->become_begin(); if (p_src) { nstring path_abs(os_path_join(sip->stack.front(), path)); trace(("rm %s\n", path_abs.c_str())); os_unlink(path_abs.get_ref()); } } break; } trace(("}\n")); } void change_remove_symlinks_to_baseline(const change::pointer &cp, user_ty::pointer up, const work_area_style_ty &style) { slink_info_ty si; if (cp->is_being_integrated() && cp->project_get()->is_a_trunk()) return; if ( !style.source_file_link && !style.source_file_symlink && !style.source_file_copy && !style.derived_file_link && !style.derived_file_symlink && !style.derived_file_copy ) return; if (!style.during_build_only) return; trace(("change_remove_symlinks_to_baseline(cp = %p)\n{\n", cp.get())); assert(cp->valid()); assert(derived_symlinks); change_verbose(cp, 0, i18n("removing symbolic links to baseline")); // // Work out the search path. // cp->search_path_get(&si.stack, false); // // walk the tree // si.cp = cp; si.up = up; si.style = &style; si.umask = cp->umask_get(); up->become_begin(); if (!dot) dot = str_from_c("."); dir_stack_walk(&si.stack, dot, unmaintain, &si, 0); up->become_end(); delete(derived_symlinks); derived_symlinks = NULL; trace(("}\n")); } // vim: set ts=8 sw=4 et :