// file : libbuild2/filesystem.hxx -*- C++ -*- // license : MIT; see accompanying LICENSE file #ifndef LIBBUILD2_FILESYSTEM_HXX #define LIBBUILD2_FILESYSTEM_HXX #include <libbutl/filesystem.hxx> #include <libbuild2/types.hxx> #include <libbuild2/utility.hxx> #include <libbuild2/context.hxx> #include <libbuild2/export.hxx> // Higher-level filesystem utilities built on top of <libbutl/filesystem.hxx>. // // Compared to the libbutl's versions, these handle errors and issue // diagnostics. Some of them also print the corresponding command line // equivalent at the specified verbosity level. Note that most of such // functions also respect the dry_run flag. // namespace build2 { using butl::auto_rmfile; using butl::auto_rmdir; // The dual interface wrapper for the {mk,rm}{file,dir}() functions // below that allows you to use it as a true/false return or a more // detailed enum from <libbutl/filesystem.hxx> // template <typename T> struct fs_status { T v; fs_status (T v): v (v) {}; operator T () const {return v;} explicit operator bool () const {return v == T::success;} }; // Set the file access and modification times (unless dry-run) to the // current time printing the standard diagnostics starting from the // specified verbosity level. If the file does not exist and create is true, // create it and fail otherwise. // LIBBUILD2_SYMEXPORT void touch (context&, const path&, bool create, uint16_t verbosity = 1); // Return the modification time for an existing regular file and // timestamp_nonexistent otherwise. Print the diagnostics and fail on system // error. // LIBBUILD2_SYMEXPORT timestamp mtime (const char*); inline timestamp mtime (const path& p) { return mtime (p.string ().c_str ()); } // Create the directory and print the standard diagnostics starting from the // specified verbosity level. // // Note that these functions ignore the dry_run flag (we might need to save // something in such a directory, such as depdb, ignoring dry_run). Overall, // it feels like we should establish the structure even for dry-run. // // Note that the implementation may not be suitable if the performance is // important and it is expected that the directory will exist in most cases. // See the fsdir{} rule for details. // using mkdir_status = butl::mkdir_status; LIBBUILD2_SYMEXPORT fs_status<mkdir_status> mkdir (const dir_path&, uint16_t verbosity = 1); LIBBUILD2_SYMEXPORT fs_status<mkdir_status> mkdir_p (const dir_path&, uint16_t verbosity = 1); // Rename a file (or file symlink) overwriting the destination if exists. // LIBBUILD2_SYMEXPORT void mvfile (const path& from, const path& to, uint16_t verbosity = 1); // Remove the file (unless dry-run) and print the standard diagnostics // starting from the specified verbosity level. The second argument is only // used in diagnostics, to print the target name. Passing the path for // target will result in the relative path being printed. // using rmfile_status = butl::rmfile_status; template <typename T> fs_status<rmfile_status> rmfile (const path&, const T& target, uint16_t verbosity = 1); fs_status<rmfile_status> rmfile (context&, const path&, uint16_t verbosity = 1); fs_status<rmfile_status> rmfile (const path&, int = 1) = delete; fs_status<rmfile_status> rmfile (const path&, uint16_t) = delete; // Similar to rmfile() but for symlinks. // LIBBUILD2_SYMEXPORT fs_status<rmfile_status> rmsymlink (context&, const path&, bool dir, uint16_t verbosity); // Similar to rmfile() but for directories (note: not -r). // using rmdir_status = butl::rmdir_status; template <typename T> fs_status<rmdir_status> rmdir (const dir_path&, const T& target, uint16_t verbosity = 1); fs_status<rmdir_status> rmdir (context&, const dir_path&, uint16_t verbosity = 1); fs_status<rmdir_status> rmdir (const dir_path&, int = 1) = delete; fs_status<rmdir_status> rmdir (const dir_path&, uint16_t) = delete; // Remove the directory recursively (unless dry-run) and print the standard // diagnostics starting from the specified verbosity level. Note that this // function returns not_empty if we try to remove a working directory. If // the dir argument is false, then the directory itself is not removed. // LIBBUILD2_SYMEXPORT fs_status<rmdir_status> rmdir_r (context& ctx, const dir_path&, bool dir = true, uint16_t verbosity = 1); fs_status<rmdir_status> rmdir_r (const dir_path&, bool = true, uint16_t = 1) = delete; // Check for a file, directory or filesystem entry existence. Print the // diagnostics and fail on system error, unless ignore_error is true. // LIBBUILD2_SYMEXPORT bool exists (const path&, bool follow_symlinks = true, bool ignore_error = false); LIBBUILD2_SYMEXPORT bool exists (const dir_path&, bool ignore_error = false); LIBBUILD2_SYMEXPORT bool entry_exists (const path&, bool follow_symlinks = false, bool ignore_error = false); // Check for a directory emptiness. Print the diagnostics and fail on system // error. // LIBBUILD2_SYMEXPORT bool empty (const dir_path&); // Directories containing .buildignore (or .build2ignore in the alternative // naming scheme) file are automatically ignored by recursive name patterns. // For now the file is just a marker and its contents don't matter. Note // that these functions ignore dry-run. // Create a directory containing an empty .buildignore file. // LIBBUILD2_SYMEXPORT fs_status<mkdir_status> mkdir_buildignore (context&, const dir_path&, const path&, uint16_t verbosity = 1); // Return true if the directory is empty or only contains the .buildignore // file. Fail if the directory doesn't exist. // LIBBUILD2_SYMEXPORT bool empty_buildignore (const dir_path&, const path&); // Remove a directory if it is empty or only contains the .buildignore file. // LIBBUILD2_SYMEXPORT fs_status<rmdir_status> rmdir_buildignore (context&, const dir_path&, const path&, uint16_t verbosity = 1); // Get/set a path permissions. // using permissions = butl::permissions; LIBBUILD2_SYMEXPORT permissions path_perms (const path&); LIBBUILD2_SYMEXPORT void path_perms (const path&, permissions); // Normalize an absolute path to an existing file that may reside outside of // any project and could involve funny filesystem business (e.g., relative // directory symlinks). For example, a C/C++ header path returned by a // compiler which could be a system header. // // We used to just normalize such a path but that could result in an invalid // path (e.g., for some system/compiler headers on CentOS 7 with Clang 3.4) // because of the symlinks (if a directory component is a symlink, then any // following `..` are resolved relative to the target; see path::normalize() // for background). // // Initially, to fix this, we realized (i.e., realpath(3)) it instead. But // that turned out also not to be quite right since now we have all the // symlinks resolved: conceptually it feels correct to keep the original // header names since that's how the user chose to arrange things and // practically this is how compilers see/report them (e.g., the GCC module // mapper). // // So now we have a pretty elaborate scheme where we try to use the // normalized path if possible and fallback to realized. Normalized paths // will work for situations where `..` does not cross symlink boundaries, // which is the sane case. And for the insane case we only really care // about out-of-project files (i.e., system/compiler headers). In other // words, if you have the insane case inside your project, then you are on // your own. // LIBBUILD2_SYMEXPORT void normalize_external (path&, const char* what); } #include <libbuild2/filesystem.ixx> #include <libbuild2/filesystem.txx> #endif // LIBBUILD2_FILESYSTEM_HXX