Module: Autoproj::Ops

Defined in:
lib/autoproj/ops/build.rb,
lib/autoproj/ops/cache.rb,
lib/autoproj/ops/tools.rb,
lib/autoproj/ops/watch.rb,
lib/autoproj/ops/which.rb,
lib/autoproj/ops/import.rb,
lib/autoproj/ops/loader.rb,
lib/autoproj/ops/install.rb,
lib/autoproj/ops/snapshot.rb,
lib/autoproj/ops/cached_env.rb,
lib/autoproj/ops/atomic_write.rb,
lib/autoproj/ops/configuration.rb,
lib/autoproj/ops/phase_reporting.rb,
lib/autoproj/ops/main_config_switcher.rb

Defined Under Namespace

Modules: Tools Classes: Build, Cache, Configuration, Import, Install, Loader, MainConfigSwitcher, PackageSetHierarchy, PhaseReporting, Snapshot, WatchAlreadyRunning

Class Method Summary collapse

Class Method Details

.atomic_write(file_name, temp_dir = File.dirname(file_name)) ⇒ Object

Write to a file atomically. Useful for situations where you don’t want other processes or threads to see half-written files.

File.atomic_write('important.file') do |file|
  file.write('hello')
end

This method needs to create a temporary file. By default it will create it in the same directory as the destination file. If you don’t like this behavior you can provide a different directory but it must be on the same physical filesystem as the file you’re trying to write.

File.atomic_write('/data/something.important', '/data/tmp') do |file|
  file.write('hello')
end

Shamelessly stolen from ActiveSupport



25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
# File 'lib/autoproj/ops/atomic_write.rb', line 25

def self.atomic_write(file_name, temp_dir = File.dirname(file_name))
    Tempfile.open(".#{File.basename(file_name)}", temp_dir) do |temp_file|
        temp_file.binmode
        yield temp_file
        temp_file.close

        old_stat = begin
            # Get original file permissions
            File.stat(file_name)
        rescue Errno::ENOENT
            # If not possible, probe which are the default permissions in the
            # destination directory.
            probe_stat_in(File.dirname(file_name))
        end

        if old_stat
            # Set correct permissions on new file
            begin
                File.chown(old_stat.uid, old_stat.gid, temp_file.path)
                # This operation will affect filesystem ACL's
                File.chmod(old_stat.mode, temp_file.path)
            rescue Errno::EPERM, Errno::EACCES
                # Changing file ownership failed, moving on.
            end
        end

        # Overwrite original file with temp file
        File.rename(temp_file.path, file_name)
    end
end

.cached_env_path(root_dir) ⇒ Object



5
6
7
# File 'lib/autoproj/ops/cached_env.rb', line 5

def self.cached_env_path(root_dir)
    File.join(root_dir, ".autoproj", "env.yml")
end

.load_cached_env(root_dir) ⇒ Object



9
10
11
12
13
14
15
16
17
# File 'lib/autoproj/ops/cached_env.rb', line 9

def self.load_cached_env(root_dir)
    path = cached_env_path(root_dir)
    if File.file?(path)
        env = YAML.safe_load(File.read(path))
        Autobuild::Environment::ExportedEnvironment.new(
            env["set"], env["unset"], env["update"]
        )
    end
end

.loaderObject

Deprecated.

use Autoproj.workspace, or better make sure all ops classes get their own workspace object as argument



107
108
109
# File 'lib/autoproj/ops/loader.rb', line 107

def self.loader
    Autoproj.workspace
end

.probe_stat_in(dir) ⇒ Object

Private utility method.



57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
# File 'lib/autoproj/ops/atomic_write.rb', line 57

def self.probe_stat_in(dir) # :nodoc:
    basename = [
        ".permissions_check",
        Thread.current.object_id,
        Process.pid,
        rand(1000000)
    ].join(".")

    file_name = File.join(dir, basename)
    FileUtils.touch(file_name)
    File.stat(file_name)
rescue Errno::ENOENT
    file_name = nil
ensure
    FileUtils.rm_f(file_name) if file_name
end

.save_cached_env(root_dir, env) ⇒ Object



19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
# File 'lib/autoproj/ops/cached_env.rb', line 19

def self.save_cached_env(root_dir, env)
    env = env.exported_environment
    path = cached_env_path(root_dir)
    existing =
        begin
            YAML.safe_load(File.read(path))
        rescue Exception
        end

    env = Hash["set" => env.set, "unset" => env.unset, "update" => env.update]
    if env != existing
        Ops.atomic_write(path) do |io|
            io.write YAML.dump(env)
        end
        true
    end
end

.watch_cleanup_marker(io) ⇒ Object



32
33
34
35
# File 'lib/autoproj/ops/watch.rb', line 32

def self.watch_cleanup_marker(io)
    FileUtils.rm_f io.path
    io.close
end

.watch_create_marker(root_dir) ⇒ Object



18
19
20
21
22
23
24
25
26
27
28
29
30
# File 'lib/autoproj/ops/watch.rb', line 18

def self.watch_create_marker(root_dir)
    io = File.open(watch_marker_path(root_dir), "a+")
    unless io.flock(File::LOCK_EX | File::LOCK_NB)
        raise WatchAlreadyRunning, "autoproj watch is already running as PID #{io.read.strip}"
    end

    io.truncate(0)
    io.puts Process.pid
    io.flush
rescue Exception
    io&.close
    raise
end

.watch_marker_path(root_dir) ⇒ Object



3
4
5
# File 'lib/autoproj/ops/watch.rb', line 3

def self.watch_marker_path(root_dir)
    File.join(root_dir, ".autoproj", "watch")
end

.watch_running?(root_dir) ⇒ Boolean

Returns:

  • (Boolean)


7
8
9
10
11
12
13
14
# File 'lib/autoproj/ops/watch.rb', line 7

def self.watch_running?(root_dir)
    io = File.open(watch_marker_path(root_dir))
    !io.flock(File::LOCK_EX | File::LOCK_NB)
rescue Errno::ENOENT
    false
ensure
    io&.close
end

.which(cmd, path_entries: nil) ⇒ String

Find the given executable file in PATH

If ‘cmd` is an absolute path, it will either return it or raise if `cmd` is not executable. Otherwise, looks for an executable named `cmd` in PATH and returns it, or raises if it cannot be found. The exception contains a more detailed reason for failure

Parameters:

  • cmd (String)

Returns:

  • (String)

    the resolved program

Raises:



19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
# File 'lib/autoproj/ops/which.rb', line 19

def self.which(cmd, path_entries: nil)
    path = Pathname.new(cmd)
    if path.absolute?
        if path.file? && path.executable?
            cmd
        elsif path.exist?
            raise ExecutableNotFound.new(cmd),
                  "given command `#{cmd}` exists but is not an executable file"
        else
            raise ExecutableNotFound.new(cmd),
                  "given command `#{cmd}` does not exist, "\
                  "an executable file was expected"
        end
    else
        path_entries = path_entries.call if path_entries.respond_to?(:call)
        absolute = Autobuild::Environment.find_executable_in_path(cmd, path_entries)

        if absolute
            absolute
        elsif (file = Autobuild::Environment.find_in_path(cmd, path_entries))
            raise ExecutableNotFound.new(cmd),
                  "`#{cmd}` resolves to #{file} which is not executable"
        else
            raise ExecutableNotFound.new(cmd),
                  "cannot resolve `#{cmd}` to an executable in the workspace"
        end
    end
end