/
CmdDependency.jl
178 lines (141 loc) · 4.81 KB
/
CmdDependency.jl
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
mutable struct CmdDependency
exec::Base.Cmd
test_args::Base.Cmd
validate_success::Bool
validate_stdout::Function
validate_stderr::Function
exit_when_fail::Bool
end
"""
# Struct
mutable struct CmdDependency
exec::Base.Cmd
test_args::Base.Cmd
validate_success::Bool
validate_stdout::Function
validate_stderr::Function
exit_when_fail::Bool
end
# Methods
CmdDependency(;
exec::Base.Cmd=``,
test_args::Base.Cmd=``,
validate_success::Bool=false,
validate_stdout::Function=do_nothing,
validate_stderr::Function=do_nothing,
exit_when_fail::Bool=true
)
Create Command Dependency (`CmdDependency`).
# Arguments
- `exec::AbstractCmd`: the command to call the dependency.
- `test_args::AbstractCmd`: for testing purposes, the command to be appended to `exec`.
- `validate_success::Bool`: when checking the dependency, whether to validate the exit code == 0.
- `validate_stdout::Function`: a function takes standard out as `String` and return the validation result as `::Bool`.
- `validate_stderr::Function`: a function takes standard error as `String` and return the validation result as `::Bool`.
- `exit_when_fail::Bool`: if validation fails, whether to throw error and exit.
# Example
julia = CmdDependency(
exec = Base.julia_cmd(),
test_args = `--version`,
validate_success = true,
validate_stdout = x -> occursin(r"^julia version", x),
validate_stderr = do_nothing,
exit_when_fail = true
)
check_dependency(julia)
"""
function CmdDependency(;
exec::Cmd=``,
test_args::Cmd=``,
validate_success::Bool=false,
validate_stdout::Function=do_nothing,
validate_stderr::Function=do_nothing,
exit_when_fail::Bool=true
)
CmdDependency(exec, test_args, validate_success, validate_stdout, validate_stderr, exit_when_fail)
end
"""
readall(cmd::Cmd) -> (standard_out::String, standard_err::String, success::Bool)
"""
function readall(cmd::Base.AbstractCmd)
out = Pipe()
err = Pipe()
process = try
run(pipeline(cmd, stdout=out, stderr=err))
catch
nothing
end
close(out.in)
close(err.in)
return (
read(out, String),
read(err, String),
!isnothing(process)
)
end
"""
check_dependency(p::CmdDependency; exit_when_fail::Bool = p.exit_when_fail) -> Bool
Check `CmdDependency` by evaluating:
`\$(p.exec) \$(p.test_args)`
If success, return `true`.
If fail, return `false`, or throw DependencyError when `exit_when_fail` set to `true`.
"""
function check_dependency(p::CmdDependency; exit_when_fail::Bool = p.exit_when_fail)
out, err, success = readall(`$p $(p.test_args)`)
has_dependency = true
if p.validate_success && !success
@goto dependency_error
end
try
res = p.validate_stdout(out)
res === false && error("DependencyError: invalid: $p: the `validate_stdout` function returns false.")
catch e
rethrow(e)
@goto dependency_error
end
try
res = p.validate_stderr(err)
res === false && error("DependencyError: invalid: $p: the `validate_stdout` function returns false.")
catch e
rethrow(e)
@goto dependency_error
end
return true
@label dependency_error
@error timestamp() * "DependencyError: invalid: $p" CHECK_ARGS=p.test_args _module=nothing _group=nothing _id=nothing _file=nothing
println(stderr, "Dependency check stdout:")
println(stderr, out)
println(stderr, "Dependency check stderr:")
println(stderr, err)
if exit_when_fail
error("DependencyError: invalid: $p")
end
return false
end
# Interpolation in Cmd
# It allows CmdDependency to be interpolated in `$p`.
Base.arg_gen(p::CmdDependency) = Base.arg_gen(p.exec)
"""
check_dependency_file(path::Union{AbstractString,Cmd}; exit_when_false=true) -> Bool
Checke whether a file exists. Return `::Bool`.
"""
function check_dependency_file(path::AbstractString; exit_when_false=true)
has_dependency = isfile(path)
if exit_when_false && !has_dependency
error("DependencyError: not a valid file: $path")
end
has_dependency
end
check_dependency_file(path::Cmd; exit_when_false=true) = check_dependency_file(str(path); exit_when_false=exit_when_false)
"""
check_dependency_dir(path::Union{AbstractString,Cmd}; exit_when_false=true) -> Bool
Checke whether a directory exists. Return `::Bool`.
"""
function check_dependency_dir(path::AbstractString; exit_when_false=true)
has_dependency = isdir(path)
if exit_when_false && !has_dependency
error("DependencyError: not a valid directory: $path")
end
has_dependency
end
check_dependency_dir(path::Cmd; exit_when_false=true) = check_dependency_file(str(path); exit_when_false=exit_when_false)