NAME

Stow - manage farms of symbolic links

SYNOPSIS

my $stow = new Stow(%$options);

$stow->plan_unstow(@pkgs_to_unstow);
$stow->plan_stow  (@pkgs_to_stow);

my %conflicts = $stow->get_conflicts;
$stow->process_tasks() unless %conflicts;

DESCRIPTION

This is the backend Perl module for GNU Stow, a program for managing the installation of software packages, keeping them separate (/usr/local/stow/emacs vs. /usr/local/stow/perl, for example) while making them appear to be installed in the same place (/usr/local).

Stow doesn't store an extra state between runs, so there's no danger of mangling directories when file hierarchies don't match the database. Also, stow will never delete any files, directories, or links that appear in a stow directory, so it is always possible to rebuild the target tree.

CONSTRUCTORS

new(%options)

Required options

  • dir - the stow directory

  • target - the target directory

Non-mandatory options

See the documentation for the stow CLI front-end for information on these.

  • conflicts

  • simulate

  • verbose

  • paranoid

  • compat

  • test_mode

  • adopt

  • no-folding

  • ignore

  • override

  • defer

N.B. This sets the current working directory to the target directory.

set_stow_dir([$dir])

Sets a new stow directory. This allows the use of multiple stow directories within one Stow instance, e.g.

$stow->plan_stow('foo');
$stow->set_stow_dir('/different/stow/dir');
$stow->plan_stow('bar');
$stow->process_tasks;

If $dir is omitted, uses the value of the dir parameter passed to the new() constructor.

METHODS

plan_unstow(@packages)

Plan which symlink/directory creation/removal tasks need to be executed in order to unstow the given packages. Any potential conflicts are then accessible via get_conflicts().

plan_stow(@packages)

Plan which symlink/directory creation/removal tasks need to be executed in order to stow the given packages. Any potential conflicts are then accessible via get_conflicts().

within_target_do($code)

Execute code within target directory, preserving cwd.

$code

Anonymous subroutine to execute within target dir.

This is done to ensure that the consumer of the Stow interface doesn't have to worry about (a) what their cwd is, and (b) that their cwd might change.

stow_contents($stow_path, $package, $pkg_subdir, $target_subdir)

Stow the contents of the given directory.

$stow_path

Relative path from current (i.e. target) directory to the stow dir containing the package to be stowed. This can differ from $self-{stow_path}> when unfolding a (sub)tree which is already stowed from a package in a different stow directory (see the "Multiple Stow Directories" section of the manual).

$package

The package whose contents are being stowed.

$pkg_subdir

Subdirectory of the installation image in the package directory which needs stowing as a symlink which points to it. This is relative to the top-level package directory.

$target_subdir

Subdirectory of the target directory which either needs a symlink to the corresponding package subdirectory in the installation image, or if it's an existing directory, it's an unfolded tree which may need to be folded or recursed into.

stow_node() and stow_contents() are mutually recursive.

stow_node($stow_path, $package, $pkg_subpath, $target_subpath)

Stow the given node

$stow_path

Relative path from current (i.e. target) directory to the stow dir containing the node to be stowed. This can differ from $self-{stow_path}> when unfolding a (sub)tree which is already stowed from a package in a different stow directory (see the "Multiple Stow Directories" section of the manual).

$package

The package containing the node being stowed.

$pkg_subpath

Subpath of the installation image in the package directory which needs stowing as a symlink which points to it. This is relative to the top-level package directory.

$target_subpath

Subpath of the target directory which either needs a symlink to the corresponding package subpathectory in the installation image, or if it's an existing directory, it's an unfolded tree which may need to be folded or recursed into.

stow_node() and stow_contents() are mutually recursive.

should_skip_target($target_subdir)

Determine whether $target_subdir is a stow directory which should not be stowed to or unstowed from. This mechanism protects stow directories from being altered by stow, and is a necessary safety check because the stow directory could live beneath the target directory.

Returns true iff target is a stow directory

cwd must be the top-level target directory, otherwise marked_stow_dir() won't work.

unstow_contents($package, $pkg_subdir, $target_subdir)

Unstow the contents of the given directory

$package

The package whose contents are being unstowed.

$pkg_subdir

Subdirectory of the installation image in the package directory which may need a symlink pointing to it to be unstowed. This is relative to the top-level package directory.

$target_subdir

Subdirectory of the target directory which either needs unstowing of a symlink to the corresponding package subdirectory in the installation image, or if it's an existing directory, it's an unfolded tree which may need to be recursed into.

unstow_node() and unstow_contents() are mutually recursive. Here we traverse the package tree, rather than the target tree.

unstow_node($package, $pkg_subpath, $target_subpath)

Unstow the given node.

$package

The package containing the node being unstowed.

$pkg_subpath

Subpath of the installation image in the package directory which needs stowing as a symlink which points to it. This is relative to the top-level package directory.

$target_subpath

Subpath of the target directory which either needs a symlink to the corresponding package subpathectory in the installation image, or if it's an existing directory, it's an unfolded tree which may need to be folded or recursed into.

unstow_node() and unstow_contents() are mutually recursive.

Determine whether the given link points to a member of a stowed package.

$target_subpath

Path to a symbolic link under current directory.

Where that link points to.

Lossy wrapper around find_stowed_path().

Returns the package iff link is owned by stow, otherwise ''.

Determine whether the given symlink within the target directory is a stowed path pointing to a member of a package under the stow dir, and if so, obtain a breakdown of information about this stowed path.

$target_subpath

Path to a symbolic link somewhere under the target directory, relative to the top-level target directory (which is also expected to be the current directory).

Where that link points to (needed because link might not exist yet due to two-phase approach, so we can't just call readlink()). If this is owned by Stow, it will be expressed relative to (the directory containing) $target_subpath. However if it's not, it could of course be relative or absolute, point absolutely anywhere, and could even be dangling.

Returns ($pkg_path_from_cwd, $stow_path, $package) where $pkg_path_from_cwd and $stow_path are relative from the top-level target directory. $pkg_path_from_cwd is the full relative path to the member of the package pointed to by $link_dest; $stow_path is the relative path to the stow directory; and $package is the name of the package; or ('', '', '') if link is not owned by stow.

cwd must be the top-level target directory, otherwise find_containing_marked_stow_dir() won't work. Allow for stow dir not being under target dir.

Detect whether symlink destination is within current stow dir

Returns ($package, $pkg_subpath) - package within the current stow dir and subpath within that package which the symlink points to.

find_containing_marked_stow_dir($pkg_path_from_cwd)

Detect whether path is within a marked stow directory

$pkg_path_from_cwd => path to directory to check

Returns ($stow_path, $package) where $stow_path is the highest directory (relative from the top-level target directory) which is marked as a Stow directory, and $package is the containing package; or ('', '') if no containing directory is marked as a stow directory.

cwd must be the top-level target directory, otherwise marked_stow_dir() won't work.

cleanup_invalid_links($dir)

Clean up orphaned links that may block folding

$dir

Path to directory to check

This is invoked by unstow_contents(). We only clean up links which are both orphaned and owned by Stow, i.e. they point to a non-existent location within a Stow package. These can block tree folding, and they can easily occur when a file in Stow package is renamed or removed, so the benefit should outweigh the low risk of actually someone wanting to keep an orphaned link to within a Stow package.

foldable($target_subdir)

Determine whether a tree can be folded

$target_subdir

Path to the target sub-directory to check for foldability, relative to the current directory (the top-level target directory).

Returns path to the parent dir iff the tree can be safely folded. The path returned is relative to the parent of $target_subdir, i.e. it can be used as the source for a replacement symlink.

fold_tree($target_subdir, $pkg_subpath)

Fold the given tree

$target_subdir

Directory that we will replace with a link to $pkg_subpath.

$pkg_subpath

link to the folded tree source

Only called iff foldable() is true so we can remove some checks.

conflict($package, $message)

Handle conflicts in stow operations

$package

the package involved with the conflicting operation

$message

a description of the conflict

get_conflicts()

Returns a nested hash of all potential conflicts discovered: the keys are actions ('stow' or 'unstow'), and the values are hashrefs whose keys are stow package names and whose values are conflict descriptions, e.g.:

(
    stow => {
        perl => [
            "existing target is not owned by stow: bin/a2p"
            "existing target is neither a link nor a directory: bin/perl"
        ]
    }
)

get_conflict_count()

Returns the number of conflicts found.

get_tasks()

Returns a list of all symlink/directory creation/removal tasks.

get_action_count()

Returns the number of actions planned for this Stow instance.

ignore($stow_path, $package, $target)

Determine if the given path matches a regex in our ignore list.

$stow_path

the stow directory containing the package

$package

the package containing the path

$target

the path to check against the ignore list relative to its package directory

Returns true iff the path should be ignored.

invalidate_memoized_regexp($file)

For efficiency of performance, regular expressions are compiled from each ignore list file the first time it is used by the Stow process, and then memoized for future use. If you expect the contents of these files to change during a single run, you will need to invalidate the memoized value from this cache. This method allows you to do that.

defer($path)

Determine if the given path matches a regex in our defer list

$path

Returns boolean.

override($path)

Determine if the given path matches a regex in our override list

$path

Returns boolean

process_tasks()

Process each task in the tasks list

none

Returns : n/a Throws : fatal error if tasks list is corrupted or a task fails

process_task($task)

Process a single task.

$task => the task to process

Returns : n/a Throws : fatal error if task fails # # Must run from within target directory. Task involve either creating or deleting dirs and symlinks an action is set to 'skip' if it is found to be redundant

Finds the link task action for the given path, if there is one

$path

Returns 'remove', 'create', or '' if there is no action. Throws a fatal exception if an invalid action is found.

dir_task_action($path)

Finds the dir task action for the given path, if there is one.

$path

Returns 'remove', 'create', or '' if there is no action. Throws a fatal exception if an invalid action is found.

Determine whether the given path or any parent thereof is a link scheduled for removal

$target_path

Returns boolean

is_a_link($target_path)

Determine if the given path is a current or planned link.

$target_path

Returns false if an existing link is scheduled for removal and true if a non-existent link is scheduled for creation.

is_a_dir($target_path)

Determine if the given path is a current or planned directory

$target_path

Returns false if an existing directory is scheduled for removal and true if a non-existent directory is scheduled for creation. We also need to be sure we are not just following a link.

is_a_node($target_path)

Determine whether the given path is a current or planned node.

$target_path

Returns false if an existing node is scheduled for removal, or true if a non-existent node is scheduled for creation. We also need to be sure we are not just following a link.

read_a_link($link)

Return the destination of a current or planned link.

Path to the link target.

Returns the destination of the given link. Throws a fatal exception if the given path is not a current or planned link.

Wrap 'link' operation for later processing

the existing file to link to

the file to link

Throws an error if this clashes with an existing planned operation. Cleans up operations that undo previous operations.

do_unlink($file)

Wrap 'unlink' operation for later processing

$file

the file to unlink

Throws an error if this clashes with an existing planned operation. Will remove an existing planned link.

do_mkdir($dir)

Wrap 'mkdir' operation

$dir

the directory to remove

Throws a fatal exception if operation fails. Outputs a message if 'verbose' option is set. Does not perform operation if 'simulate' option is set. Cleans up operations that undo previous operations.

do_rmdir($dir)

Wrap 'rmdir' operation

$dir

the directory to remove

Throws a fatal exception if operation fails. Outputs a message if 'verbose' option is set. Does not perform operation if 'simulate' option is set.

do_mv($src, $dst)

Wrap 'move' operation for later processing.

$src

the file to move

$dst

the path to move it to

Throws an error if this clashes with an existing planned operation. Alters contents of package installation image in stow dir.

$message => error message to output

Returns : n/a Throws : n/a

BUGS

SEE ALSO