Rework builder interface to only use #run method - close #91
The builder's #run method will be called repeatedly until it returns true or false. The Builder#wait_for method can be used to cause a builder to wait for a Thread, Command, or another Builder.
This commit is contained in:
1 parent
8426a54a57
commit
b882f8de99
38 files changed
+580
-665
No files matched your search
@@ -21,30 +21,23 @@ module Rscons
|
||||
|
||||
# Run the builder to produce a build target.
|
||||
def run(options)
|
||||
@vars["_TARGET"] = @target
|
||||
@vars["_SOURCES"] = @sources
|
||||
cmd =
|
||||
case
|
||||
when @sources.first.end_with?(*@env.expand_varref("${LEXSUFFIX}"))
|
||||
"LEX"
|
||||
when @sources.first.end_with?(*@env.expand_varref("${YACCSUFFIX}"))
|
||||
"YACC"
|
||||
else
|
||||
raise "Unknown source file #{@sources.first.inspect} for CFile builder"
|
||||
end
|
||||
command = @env.build_command("${#{cmd}_CMD}", @vars)
|
||||
standard_threaded_build("#{cmd} #{@target}", @target, command, @sources, @env, @cache)
|
||||
end
|
||||
|
||||
# Finalize a build.
|
||||
#
|
||||
# @param options [Hash]
|
||||
# Finalize options.
|
||||
#
|
||||
# @return [String, nil]
|
||||
# The target name on success or nil on failure.
|
||||
def finalize(options)
|
||||
standard_finalize(options)
|
||||
if @command
|
||||
finalize_command
|
||||
else
|
||||
@vars["_TARGET"] = @target
|
||||
@vars["_SOURCES"] = @sources
|
||||
cmd =
|
||||
case
|
||||
when @sources.first.end_with?(*@env.expand_varref("${LEXSUFFIX}"))
|
||||
"LEX"
|
||||
when @sources.first.end_with?(*@env.expand_varref("${YACCSUFFIX}"))
|
||||
"YACC"
|
||||
else
|
||||
raise "Unknown source file #{@sources.first.inspect} for CFile builder"
|
||||
end
|
||||
command = @env.build_command("${#{cmd}_CMD}", @vars)
|
||||
standard_command("#{cmd} #{@target}", command)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
module Rscons
|
||||
module Builders
|
||||
# Execute a command that will produce the given target based on the given
|
||||
# sources.
|
||||
# A builder to execute an arbitrary command that will produce the given
|
||||
# target based on the given sources.
|
||||
#
|
||||
# @since 1.8.0
|
||||
#
|
||||
@@ -11,33 +11,20 @@ module Rscons
|
||||
class Command < Builder
|
||||
|
||||
# Run the builder to produce a build target.
|
||||
#
|
||||
# @param options [Hash] Builder run options.
|
||||
#
|
||||
# @return [String, ThreadedCommand]
|
||||
# Target file name if target is up to date or a {ThreadedCommand}
|
||||
# to execute to build the target.
|
||||
def run(options)
|
||||
@vars["_TARGET"] = @target
|
||||
@vars["_SOURCES"] = @sources
|
||||
command = @env.build_command("${CMD}", @vars)
|
||||
cmd_desc = @vars["CMD_DESC"] || "Command"
|
||||
options = {}
|
||||
if @vars["CMD_STDOUT"]
|
||||
options[:stdout] = @env.expand_varref("${CMD_STDOUT}", @vars)
|
||||
if @command
|
||||
finalize_command
|
||||
else
|
||||
@vars["_TARGET"] = @target
|
||||
@vars["_SOURCES"] = @sources
|
||||
command = @env.build_command("${CMD}", @vars)
|
||||
cmd_desc = @vars["CMD_DESC"] || "Command"
|
||||
options = {}
|
||||
if @vars["CMD_STDOUT"]
|
||||
options[:stdout] = @env.expand_varref("${CMD_STDOUT}", @vars)
|
||||
end
|
||||
standard_command("#{cmd_desc} #{@target}", command, options)
|
||||
end
|
||||
standard_threaded_build("#{cmd_desc} #{@target}", @target, command, @sources, @env, @cache, options)
|
||||
end
|
||||
|
||||
# Finalize a build.
|
||||
#
|
||||
# @param options [Hash]
|
||||
# Finalize options.
|
||||
#
|
||||
# @return [String, nil]
|
||||
# The target name on success or nil on failure.
|
||||
def finalize(options)
|
||||
standard_finalize(options)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -6,14 +6,14 @@ module Rscons
|
||||
# Run the builder to produce a build target.
|
||||
def run(options)
|
||||
if File.directory?(@target)
|
||||
@target
|
||||
true
|
||||
elsif File.exists?(@target)
|
||||
Ansi.write($stderr, :red, "Error: `#{@target}' already exists and is not a directory", :reset, "\n")
|
||||
false
|
||||
else
|
||||
@env.print_builder_run_message("Directory #{@target}", nil)
|
||||
@cache.mkdir_p(@target)
|
||||
@target
|
||||
true
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -11,30 +11,15 @@ module Rscons
|
||||
|
||||
# Run the builder to produce a build target.
|
||||
def run(options)
|
||||
@vars["_SOURCES"] = sources
|
||||
command = @env.build_command("${DISASM_CMD}", @vars)
|
||||
if @cache.up_to_date?(@target, command, @sources, @env)
|
||||
@target
|
||||
if @command
|
||||
finalize_command
|
||||
else
|
||||
@cache.mkdir_p(File.dirname(@target))
|
||||
ThreadedCommand.new(
|
||||
command,
|
||||
short_description: "Disassemble #{@target}",
|
||||
system_options: {out: @target})
|
||||
@vars["_SOURCES"] = @sources
|
||||
command = @env.build_command("${DISASM_CMD}", @vars)
|
||||
standard_command("Disassemble #{target}", command, stdout: @target)
|
||||
end
|
||||
end
|
||||
|
||||
# Finalize a build.
|
||||
#
|
||||
# @param options [Hash]
|
||||
# Finalize options.
|
||||
#
|
||||
# @return [String, nil]
|
||||
# The target name on success or nil on failure.
|
||||
def finalize(options)
|
||||
standard_finalize(options)
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -42,7 +42,7 @@ module Rscons
|
||||
end
|
||||
@cache.register_build(dest, :Copy, [src], @env)
|
||||
end
|
||||
@target if (target_is_dir ? Dir.exists?(@target) : File.exists?(@target))
|
||||
(target_is_dir ? Dir.exists?(@target) : File.exists?(@target)) ? true : false
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -11,17 +11,6 @@ module Rscons
|
||||
)
|
||||
|
||||
# Create an instance of the Builder to build a target.
|
||||
#
|
||||
# @param options [Hash]
|
||||
# Options.
|
||||
# @option options [String] :target
|
||||
# Target file name.
|
||||
# @option options [Array<String>] :sources
|
||||
# Source file name(s).
|
||||
# @option options [Environment] :env
|
||||
# The Environment executing the builder.
|
||||
# @option options [Hash,VarSet] :vars
|
||||
# Extra construction variables.
|
||||
def initialize(options)
|
||||
super(options)
|
||||
suffixes = @env.expand_varref(["${OBJSUFFIX}", "${LIBSUFFIX}"], @vars)
|
||||
@@ -30,29 +19,15 @@ module Rscons
|
||||
end
|
||||
|
||||
# Run the builder to produce a build target.
|
||||
#
|
||||
# @param options [Hash] Builder run options.
|
||||
#
|
||||
# @return [String,false]
|
||||
# Name of the target file on success or false on failure.
|
||||
def run(options)
|
||||
@vars["_TARGET"] = @target
|
||||
@vars["_SOURCES"] = @objects
|
||||
command = @env.build_command("${ARCMD}", @vars)
|
||||
standard_threaded_build("AR #{@target}", @target, command, @objects, @env, @cache)
|
||||
end
|
||||
|
||||
# Finalize a build.
|
||||
#
|
||||
# @param options [Hash]
|
||||
# Finalize options.
|
||||
#
|
||||
# @return [String, nil]
|
||||
# The target name on success or nil on failure.
|
||||
def finalize(options)
|
||||
if options[:command_status]
|
||||
@cache.register_build(@target, options[:tc].command, @objects, @env)
|
||||
@target
|
||||
if @command
|
||||
finalize_command(sources: @objects)
|
||||
true
|
||||
else
|
||||
@vars["_TARGET"] = @target
|
||||
@vars["_SOURCES"] = @objects
|
||||
command = @env.build_command("${ARCMD}", @vars)
|
||||
standard_command("AR #{@target}", command, sources: @objects)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -76,40 +76,26 @@ module Rscons
|
||||
end
|
||||
|
||||
# Run the builder to produce a build target.
|
||||
#
|
||||
# @param options [Hash] Builder run options.
|
||||
#
|
||||
# @return [String, ThreadedCommand]
|
||||
# Target file name if target is up to date or a {ThreadedCommand}
|
||||
# to execute to build the target.
|
||||
def run(options)
|
||||
@vars["_TARGET"] = @target
|
||||
@vars["_SOURCES"] = @sources
|
||||
@vars["_DEPFILE"] = Rscons.set_suffix(target, env.expand_varref("${DEPFILESUFFIX}", vars))
|
||||
com_prefix = KNOWN_SUFFIXES.find do |compiler, suffix_var|
|
||||
@sources.first.end_with?(*@env.expand_varref("${#{suffix_var}}", @vars))
|
||||
end.tap do |v|
|
||||
v.nil? and raise "Error: unknown input file type: #{@sources.first.inspect}"
|
||||
end.first
|
||||
command = @env.build_command("${#{com_prefix}CMD}", @vars)
|
||||
@env.produces(@target, @vars["_DEPFILE"])
|
||||
standard_threaded_build("#{com_prefix} #{@target}", @target, command, @sources, @env, @cache)
|
||||
end
|
||||
|
||||
# Finalize the build operation.
|
||||
#
|
||||
# @param options [Hash] Builder finalize options.
|
||||
#
|
||||
# @return [String, nil]
|
||||
# Name of the target file on success or nil on failure.
|
||||
def finalize(options)
|
||||
if options[:command_status]
|
||||
if @command
|
||||
deps = @sources
|
||||
if File.exists?(@vars['_DEPFILE'])
|
||||
deps += Util.parse_makefile_deps(@vars['_DEPFILE'])
|
||||
if File.exists?(@vars["_DEPFILE"])
|
||||
deps += Util.parse_makefile_deps(@vars["_DEPFILE"])
|
||||
end
|
||||
@cache.register_build(@target, options[:tc].command, deps.uniq, @env)
|
||||
@target
|
||||
@cache.register_build(@target, @command, deps.uniq, @env)
|
||||
true
|
||||
else
|
||||
@vars["_TARGET"] = @target
|
||||
@vars["_SOURCES"] = @sources
|
||||
@vars["_DEPFILE"] = Rscons.set_suffix(target, env.expand_varref("${DEPFILESUFFIX}", vars))
|
||||
com_prefix = KNOWN_SUFFIXES.find do |compiler, suffix_var|
|
||||
@sources.first.end_with?(*@env.expand_varref("${#{suffix_var}}", @vars))
|
||||
end.tap do |v|
|
||||
v.nil? and raise "Error: unknown input file type: #{@sources.first.inspect}"
|
||||
end.first
|
||||
command = @env.build_command("${#{com_prefix}CMD}", @vars)
|
||||
@env.produces(@target, @vars["_DEPFILE"])
|
||||
standard_command("#{com_prefix} #{@target}", command)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -12,44 +12,30 @@ module Rscons
|
||||
)
|
||||
|
||||
# Run the builder to produce a build target.
|
||||
#
|
||||
# @param options [Hash] Builder run options.
|
||||
#
|
||||
# @return [String, ThreadedCommand]
|
||||
# Target file name if target is up to date or a {ThreadedCommand}
|
||||
# to execute to build the target.
|
||||
def run(options)
|
||||
if @sources.find {|s| s.end_with?(*@env.expand_varref("${CXXSUFFIX}", @vars))}
|
||||
pp_cc = "${CXX}"
|
||||
depgen = "${CXXDEPGEN}"
|
||||
else
|
||||
pp_cc = "${CC}"
|
||||
depgen = "${CCDEPGEN}"
|
||||
end
|
||||
@vars["_PREPROCESS_CC"] = pp_cc
|
||||
@vars["_PREPROCESS_DEPGEN"] = depgen
|
||||
@vars["_TARGET"] = @target
|
||||
@vars["_SOURCES"] = @sources
|
||||
@vars["_DEPFILE"] = Rscons.set_suffix(target, env.expand_varref("${DEPFILESUFFIX}", vars))
|
||||
command = @env.build_command("${CPP_CMD}", @vars)
|
||||
@env.produces(@target, @vars["_DEPFILE"])
|
||||
standard_threaded_build("#{name} #{@target}", @target, command, @sources, @env, @cache)
|
||||
end
|
||||
|
||||
# Finalize the build operation.
|
||||
#
|
||||
# @param options [Hash] Builder finalize options.
|
||||
#
|
||||
# @return [String, nil]
|
||||
# Name of the target file on success or nil on failure.
|
||||
def finalize(options)
|
||||
if options[:command_status]
|
||||
if @command
|
||||
deps = @sources
|
||||
if File.exists?(@vars['_DEPFILE'])
|
||||
deps += Util.parse_makefile_deps(@vars['_DEPFILE'])
|
||||
if File.exists?(@vars["_DEPFILE"])
|
||||
deps += Util.parse_makefile_deps(@vars["_DEPFILE"])
|
||||
end
|
||||
@cache.register_build(@target, options[:tc].command, deps.uniq, @env)
|
||||
@target
|
||||
@cache.register_build(@target, @command, deps.uniq, @env)
|
||||
true
|
||||
else
|
||||
if @sources.find {|s| s.end_with?(*@env.expand_varref("${CXXSUFFIX}", @vars))}
|
||||
pp_cc = "${CXX}"
|
||||
depgen = "${CXXDEPGEN}"
|
||||
else
|
||||
pp_cc = "${CC}"
|
||||
depgen = "${CCDEPGEN}"
|
||||
end
|
||||
@vars["_PREPROCESS_CC"] = pp_cc
|
||||
@vars["_PREPROCESS_DEPGEN"] = depgen
|
||||
@vars["_TARGET"] = @target
|
||||
@vars["_SOURCES"] = @sources
|
||||
@vars["_DEPFILE"] = Rscons.set_suffix(target, env.expand_varref("${DEPFILESUFFIX}", vars))
|
||||
command = @env.build_command("${CPP_CMD}", @vars)
|
||||
@env.produces(@target, @vars["_DEPFILE"])
|
||||
standard_command("#{name} #{@target}", command)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -40,40 +40,26 @@ module Rscons
|
||||
end
|
||||
|
||||
# Run the builder to produce a build target.
|
||||
#
|
||||
# @param options [Hash] Builder run options.
|
||||
#
|
||||
# @return [String,false]
|
||||
# Name of the target file on success or false on failure.
|
||||
def run(options)
|
||||
ld = @env.expand_varref("${LD}", @vars)
|
||||
ld = if ld != ""
|
||||
ld
|
||||
elsif @sources.find {|s| s.end_with?(*@env.expand_varref("${DSUFFIX}", @vars))}
|
||||
"${DC}"
|
||||
elsif @sources.find {|s| s.end_with?(*@env.expand_varref("${CXXSUFFIX}", @vars))}
|
||||
"${CXX}"
|
||||
else
|
||||
"${CC}"
|
||||
end
|
||||
@vars["_TARGET"] = @target
|
||||
@vars["_SOURCES"] = @objects
|
||||
@vars["LD"] = ld
|
||||
command = @env.build_command("${LDCMD}", @vars)
|
||||
standard_threaded_build("LD #{@target}", @target, command, @objects, @env, @cache)
|
||||
end
|
||||
|
||||
# Finalize a build.
|
||||
#
|
||||
# @param options [Hash]
|
||||
# Finalize options.
|
||||
#
|
||||
# @return [String, nil]
|
||||
# The target name on success or nil on failure.
|
||||
def finalize(options)
|
||||
if options[:command_status]
|
||||
@cache.register_build(@target, options[:tc].command, @objects, @env)
|
||||
@target
|
||||
if @command
|
||||
finalize_command(sources: @objects)
|
||||
true
|
||||
else
|
||||
ld = @env.expand_varref("${LD}", @vars)
|
||||
ld = if ld != ""
|
||||
ld
|
||||
elsif @sources.find {|s| s.end_with?(*@env.expand_varref("${DSUFFIX}", @vars))}
|
||||
"${DC}"
|
||||
elsif @sources.find {|s| s.end_with?(*@env.expand_varref("${CXXSUFFIX}", @vars))}
|
||||
"${CXX}"
|
||||
else
|
||||
"${CC}"
|
||||
end
|
||||
@vars["_TARGET"] = @target
|
||||
@vars["_SOURCES"] = @objects
|
||||
@vars["LD"] = ld
|
||||
command = @env.build_command("${LDCMD}", @vars)
|
||||
standard_command("LD #{@target}", command, sources: @objects)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -25,17 +25,6 @@ module Rscons
|
||||
end
|
||||
|
||||
# Create an instance of the Builder to build a target.
|
||||
#
|
||||
# @param options [Hash]
|
||||
# Options.
|
||||
# @option options [String] :target
|
||||
# Target file name.
|
||||
# @option options [Array<String>] :sources
|
||||
# Source file name(s).
|
||||
# @option options [Environment] :env
|
||||
# The Environment executing the builder.
|
||||
# @option options [Hash,VarSet] :vars
|
||||
# Extra construction variables.
|
||||
def initialize(options)
|
||||
super(options)
|
||||
libprefix = @env.expand_varref("${SHLIBPREFIX}", @vars)
|
||||
@@ -52,40 +41,26 @@ module Rscons
|
||||
end
|
||||
|
||||
# Run the builder to produce a build target.
|
||||
#
|
||||
# @param options [Hash] Builder run options.
|
||||
#
|
||||
# @return [String,false]
|
||||
# Name of the target file on success or false on failure.
|
||||
def run(options)
|
||||
ld = @env.expand_varref("${SHLD}", @vars)
|
||||
ld = if ld != ""
|
||||
ld
|
||||
elsif @sources.find {|s| s.end_with?(*@env.expand_varref("${DSUFFIX}", @vars))}
|
||||
"${SHDC}"
|
||||
elsif @sources.find {|s| s.end_with?(*@env.expand_varref("${CXXSUFFIX}", @vars))}
|
||||
"${SHCXX}"
|
||||
else
|
||||
"${SHCC}"
|
||||
end
|
||||
@vars["_TARGET"] = @target
|
||||
@vars["_SOURCES"] = @objects
|
||||
@vars["SHLD"] = ld
|
||||
command = @env.build_command("${SHLDCMD}", @vars)
|
||||
standard_threaded_build("SHLD #{@target}", @target, command, @objects, @env, @cache)
|
||||
end
|
||||
|
||||
# Finalize a build.
|
||||
#
|
||||
# @param options [Hash]
|
||||
# Finalize options.
|
||||
#
|
||||
# @return [String, nil]
|
||||
# The target name on success or nil on failure.
|
||||
def finalize(options)
|
||||
if options[:command_status]
|
||||
@cache.register_build(@target, options[:tc].command, @objects, @env)
|
||||
@target
|
||||
if @command
|
||||
finalize_command(sources: @objects)
|
||||
true
|
||||
else
|
||||
ld = @env.expand_varref("${SHLD}", @vars)
|
||||
ld = if ld != ""
|
||||
ld
|
||||
elsif @sources.find {|s| s.end_with?(*@env.expand_varref("${DSUFFIX}", @vars))}
|
||||
"${SHDC}"
|
||||
elsif @sources.find {|s| s.end_with?(*@env.expand_varref("${CXXSUFFIX}", @vars))}
|
||||
"${SHCXX}"
|
||||
else
|
||||
"${SHCC}"
|
||||
end
|
||||
@vars["_TARGET"] = @target
|
||||
@vars["_SOURCES"] = @objects
|
||||
@vars["SHLD"] = ld
|
||||
command = @env.build_command("${SHLDCMD}", @vars)
|
||||
standard_command("SHLD #{@target}", command, sources: @objects)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -61,40 +61,26 @@ module Rscons
|
||||
end
|
||||
|
||||
# Run the builder to produce a build target.
|
||||
#
|
||||
# @param options [Hash] Builder run options.
|
||||
#
|
||||
# @return [String, ThreadedCommand]
|
||||
# Target file name if target is up to date or a {ThreadedCommand}
|
||||
# to execute to build the target.
|
||||
def run(options)
|
||||
@vars["_TARGET"] = @target
|
||||
@vars["_SOURCES"] = @sources
|
||||
@vars["_DEPFILE"] = Rscons.set_suffix(target, env.expand_varref("${DEPFILESUFFIX}", vars))
|
||||
com_prefix = KNOWN_SUFFIXES.find do |compiler, suffix_var|
|
||||
@sources.first.end_with?(*@env.expand_varref("${#{suffix_var}}", @vars))
|
||||
end.tap do |v|
|
||||
v.nil? and raise "Error: unknown input file type: #{@sources.first.inspect}"
|
||||
end.first
|
||||
command = @env.build_command("${#{com_prefix}CMD}", @vars)
|
||||
@env.produces(@target, @vars["_DEPFILE"])
|
||||
standard_threaded_build("#{com_prefix} #{@target}", @target, command, @sources, @env, @cache)
|
||||
end
|
||||
|
||||
# Finalize the build operation.
|
||||
#
|
||||
# @param options [Hash] Builder finalize options.
|
||||
#
|
||||
# @return [String, nil]
|
||||
# Name of the target file on success or nil on failure.
|
||||
def finalize(options)
|
||||
if options[:command_status]
|
||||
if @command
|
||||
deps = @sources
|
||||
if File.exists?(@vars['_DEPFILE'])
|
||||
deps += Util.parse_makefile_deps(@vars['_DEPFILE'])
|
||||
if File.exists?(@vars["_DEPFILE"])
|
||||
deps += Util.parse_makefile_deps(@vars["_DEPFILE"])
|
||||
end
|
||||
@cache.register_build(@target, options[:tc].command, deps.uniq, @env)
|
||||
@target
|
||||
@cache.register_build(@target, @command, deps.uniq, @env)
|
||||
true
|
||||
else
|
||||
@vars["_TARGET"] = @target
|
||||
@vars["_SOURCES"] = @sources
|
||||
@vars["_DEPFILE"] = Rscons.set_suffix(target, env.expand_varref("${DEPFILESUFFIX}", vars))
|
||||
com_prefix = KNOWN_SUFFIXES.find do |compiler, suffix_var|
|
||||
@sources.first.end_with?(*@env.expand_varref("${#{suffix_var}}", @vars))
|
||||
end.tap do |v|
|
||||
v.nil? and raise "Error: unknown input file type: #{@sources.first.inspect}"
|
||||
end.first
|
||||
command = @env.build_command("${#{com_prefix}CMD}", @vars)
|
||||
@env.produces(@target, @vars["_DEPFILE"])
|
||||
standard_command("#{com_prefix} #{@target}", command)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -5,8 +5,14 @@ module Rscons
|
||||
# @since 1.8.0
|
||||
class SimpleBuilder < Builder
|
||||
|
||||
# @return [String]
|
||||
# User-provided builder name.
|
||||
attr_reader :name
|
||||
|
||||
# Create an instance of the Builder to build a target.
|
||||
#
|
||||
# @param name [String]
|
||||
# User-provided builder name.
|
||||
# @param options [Hash]
|
||||
# Options.
|
||||
# @option options [String] :target
|
||||
@@ -20,7 +26,8 @@ module Rscons
|
||||
# @param run_proc [Proc]
|
||||
# A Proc to execute when the builder runs. The provided block must
|
||||
# provide the have the same signature as {Builder#run}.
|
||||
def initialize(options, &run_proc)
|
||||
def initialize(name, options, &run_proc)
|
||||
@name = name
|
||||
super(options)
|
||||
@run_proc = run_proc
|
||||
end
|
||||
|
||||
Reference in new issue
Block a user