Replace CFile builder with Yacc and Lex builders - close #166

This commit is contained in:
Josh Holtrop 2023-02-22 20:57:00 -05:00
parent d4ec07dd7a
commit ed7408367f
12 changed files with 86 additions and 71 deletions

View File

@ -1,4 +0,0 @@
env do |env|
env.CFile("lexer.c", "lexer.l")
env.CFile("parser.c", "parser.y")
end

View File

@ -1,3 +0,0 @@
env do |env|
env.CFile("file.c", "foo.bar")
end

View File

@ -0,0 +1,4 @@
env do |env|
env.Lex("lexer.c", "lexer.l")
env.Yacc("parser.c", "parser.y")
end

View File

@ -925,11 +925,11 @@ There are several default builders that are built-in to Rscons:
* `Command`, which executes a user-defined command to produce the target. * `Command`, which executes a user-defined command to produce the target.
* `Copy`, which copies files or directories to a specified destination. * `Copy`, which copies files or directories to a specified destination.
* `CFile`, which builds a C or C++ source file from a lex or yacc input file.
* `Directory`, which creates a directory. * `Directory`, which creates a directory.
* `Disassemble`, which disassembles an object file to a disassembly listing. * `Disassemble`, which disassembles an object file to a disassembly listing.
* `Install`, which installs files or directories to a specified destination. * `Install`, which installs files or directories to a specified destination.
* `InstallDirectory`, which creates a directory in an install destination. * `InstallDirectory`, which creates a directory in an install destination.
* `Lex`, which builds a source file from a lex input file.
* `Library`, which collects object files into a static library archive file. * `Library`, which collects object files into a static library archive file.
* `Object`, which compiles source files to produce an object file. * `Object`, which compiles source files to produce an object file.
* `Preprocess`, which invokes the C/C++ preprocessor on a source file. * `Preprocess`, which invokes the C/C++ preprocessor on a source file.
@ -939,6 +939,7 @@ There are several default builders that are built-in to Rscons:
* `SharedObject`, which compiles source files to produce an object file, in a * `SharedObject`, which compiles source files to produce an object file, in a
way that is able to be used to create a shared library. way that is able to be used to create a shared library.
* `Size`, which runs the 'size' utility on an executable file. * `Size`, which runs the 'size' utility on an executable file.
* `Yacc`, which builds a source file from a yacc input file.
####> The Command Builder ####> The Command Builder
@ -965,17 +966,6 @@ The `Command` builder supports the following construction variables:
`CMD_STDOUT` is expanded for variable references, so the token `${_TARGET}` `CMD_STDOUT` is expanded for variable references, so the token `${_TARGET}`
can be used, for example. can be used, for example.
####> The CFile Builder
```ruby
env.CFile(target, source)
# Example
env.CFile("^/parser/parser.c", "parser.y")
```
The `CFile` builder will generate a C or C++ source file from a lex (.l, .ll)
or yacc (.y, .yy) input file.
####> The Copy Builder ####> The Copy Builder
```ruby ```ruby
@ -1048,6 +1038,17 @@ the `InstallDirectory` builder.
The `uninstall` task removes targets created by the `InstallDirectory` builder The `uninstall` task removes targets created by the `InstallDirectory` builder
but not by the `Directory` builder. but not by the `Directory` builder.
####> The Lex Builder
```ruby
env.Lex(target, source)
# Example
env.Lex("^/lexer.c", "lexer.l")
```
The `Lex` builder will generate a source file from a lex (.l, .ll)
input file.
####> The Library Builder ####> The Library Builder
```ruby ```ruby
@ -1178,6 +1179,17 @@ stores its output in the target file.
The size executable can be specified with the `SIZE` construction variable, The size executable can be specified with the `SIZE` construction variable,
and flags can be specified with `SIZEFLAGS`. and flags can be specified with `SIZEFLAGS`.
####> The Yacc Builder
```ruby
env.Yacc(target, source)
# Example
env.Yacc("^/parser.c", "parser.y")
```
The `Yacc` builder will generate a source file from a yacc (.y, .yy)
input file.
###> Phony Targets ###> Phony Targets
Rscons supports phony build targets. Rscons supports phony build targets.
@ -1959,7 +1971,7 @@ end
```ruby ```ruby
env do |env| env do |env|
env.CFile("^/parser.tab.cc", "parser.yy") env.Yacc("^/parser.tab.cc", "parser.yy")
end end
``` ```

View File

@ -23,13 +23,13 @@ module Rscons
# Names of the default builders which will be added to all newly created # Names of the default builders which will be added to all newly created
# {Environment} objects. # {Environment} objects.
DEFAULT_BUILDERS = [ DEFAULT_BUILDERS = [
:CFile,
:Command, :Command,
:Copy, :Copy,
:Directory, :Directory,
:Disassemble, :Disassemble,
:Install, :Install,
:InstallDirectory, :InstallDirectory,
:Lex,
:Library, :Library,
:Object, :Object,
:Preprocess, :Preprocess,
@ -37,6 +37,7 @@ module Rscons
:SharedLibrary, :SharedLibrary,
:SharedObject, :SharedObject,
:Size, :Size,
:Yacc,
] ]
# Class to represent a fatal error during an Rscons operation. # Class to represent a fatal error during an Rscons operation.
@ -135,11 +136,11 @@ require_relative "rscons/builders/mixins/object_deps"
require_relative "rscons/builders/mixins/program" require_relative "rscons/builders/mixins/program"
# default builders # default builders
require_relative "rscons/builders/cfile"
require_relative "rscons/builders/command" require_relative "rscons/builders/command"
require_relative "rscons/builders/copy" require_relative "rscons/builders/copy"
require_relative "rscons/builders/directory" require_relative "rscons/builders/directory"
require_relative "rscons/builders/disassemble" require_relative "rscons/builders/disassemble"
require_relative "rscons/builders/lex"
require_relative "rscons/builders/library" require_relative "rscons/builders/library"
require_relative "rscons/builders/object" require_relative "rscons/builders/object"
require_relative "rscons/builders/preprocess" require_relative "rscons/builders/preprocess"
@ -148,6 +149,7 @@ require_relative "rscons/builders/shared_library"
require_relative "rscons/builders/shared_object" require_relative "rscons/builders/shared_object"
require_relative "rscons/builders/simple_builder" require_relative "rscons/builders/simple_builder"
require_relative "rscons/builders/size" require_relative "rscons/builders/size"
require_relative "rscons/builders/yacc"
# language support # language support
require_relative "rscons/builders/lang/asm" require_relative "rscons/builders/lang/asm"

View File

@ -1,35 +0,0 @@
module Rscons
module Builders
# Build a C or C++ source file given a lex (.l, .ll) or yacc (.y, .yy)
# input file.
#
# Examples::
# env.CFile("parser.tab.cc", "parser.yy")
# env.CFile("lex.yy.cc", "parser.ll")
class CFile < Builder
# Run the builder to produce a build target.
def run(options)
if @command
finalize_command
else
@vars["_TARGET"] = @target
@vars["_SOURCES"] = @sources
case
when @sources.first.end_with?(*@env.expand_varref("${LEXSUFFIX}"))
cmd = "LEX"
message = "Generating lexer"
when @sources.first.end_with?(*@env.expand_varref("${YACCSUFFIX}"))
cmd = "YACC"
message = "Generating parser"
else
raise "Unknown source file #{@sources.first.inspect} for CFile builder"
end
command = @env.build_command("${#{cmd}_CMD}", @vars)
standard_command("#{message} from <source>#{Util.short_format_paths(@sources)}<reset> => <target>#{@target}<reset>", command)
end
end
end
end
end

View File

@ -0,0 +1,24 @@
module Rscons
module Builders
# Build a source file given a lex input file.
#
# Examples::
# env.Lex("lex.c", "parser.l")
# env.Lex("lex.cc", "parser.ll")
class Lex < Builder
# Run the builder to produce a build target.
def run(options)
if @command
finalize_command
else
@vars["_TARGET"] = @target
@vars["_SOURCES"] = @sources
command = @env.build_command("${LEX_CMD}", @vars)
standard_command("Generating lexer source from <source>#{Util.short_format_paths(@sources)}<reset> => <target>#{@target}<reset>", command)
end
end
end
end
end

View File

@ -0,0 +1,24 @@
module Rscons
module Builders
# Build a source file given a yacc input file.
#
# Examples::
# env.Yacc("parser.c", "parser.y")
# env.Yacc("parser.cc", "parser.yy")
class Yacc < Builder
# Run the builder to produce a build target.
def run(options)
if @command
finalize_command
else
@vars["_TARGET"] = @target
@vars["_SOURCES"] = @sources
command = @env.build_command("${YACC_CMD}", @vars)
standard_command("Generating parser source from <source>#{Util.short_format_paths(@sources)}<reset> => <target>#{@target}<reset>", command)
end
end
end
end
end

View File

@ -49,7 +49,6 @@ module Rscons
"LDCMD" => %w[${LD} -o ${_TARGET} ${LDFLAGS} ${_SOURCES} ${LIBDIRPREFIX}${LIBPATH} ${LIBLINKPREFIX}${LIBS}], "LDCMD" => %w[${LD} -o ${_TARGET} ${LDFLAGS} ${_SOURCES} ${LIBDIRPREFIX}${LIBPATH} ${LIBLINKPREFIX}${LIBS}],
"LDFLAGS" => [], "LDFLAGS" => [],
"LEX" => "flex", "LEX" => "flex",
"LEXSUFFIX" => %w[.l .ll],
"LEX_CMD" => %w[${LEX} ${LEX_FLAGS} -o ${_TARGET} ${_SOURCES}], "LEX_CMD" => %w[${LEX} ${LEX_FLAGS} -o ${_TARGET} ${_SOURCES}],
"LEX_FLAGS" => [], "LEX_FLAGS" => [],
"LIBDIRPREFIX" => "-L", "LIBDIRPREFIX" => "-L",
@ -84,7 +83,6 @@ module Rscons
"SIZECMD" => %w[${SIZE} ${SIZEFLAGS} ${_SOURCES}], "SIZECMD" => %w[${SIZE} ${SIZEFLAGS} ${_SOURCES}],
"SIZEFLAGS" => [], "SIZEFLAGS" => [],
"YACC" => "bison", "YACC" => "bison",
"YACCSUFFIX" => %w[.y .yy],
"YACC_CMD" => %w[${YACC} ${YACC_FLAGS} -o ${_TARGET} ${_SOURCES}], "YACC_CMD" => %w[${YACC} ${YACC_FLAGS} -o ${_TARGET} ${_SOURCES}],
"YACC_FLAGS" => %w[-d], "YACC_FLAGS" => %w[-d],
} }

View File

@ -1163,28 +1163,21 @@ EOF
end end
end end
context "CFile builder" do context "Lex and Yacc builders" do
it "builds a .c file using flex and bison" do it "builds C files using flex and bison" do
test_dir("cfile") test_dir("lex_yacc")
result = run_rscons result = run_rscons
expect(result.stderr).to eq "" expect(result.stderr).to eq ""
verify_lines(lines(result.stdout), [ verify_lines(lines(result.stdout), [
%r{Generating lexer from lexer.l => lexer.c}, %r{Generating lexer source from lexer.l => lexer.c},
%r{Generating parser from parser.y => parser.c}, %r{Generating parser source from parser.y => parser.c},
]) ])
result = run_rscons result = run_rscons
expect(result.stderr).to eq "" expect(result.stderr).to eq ""
expect(result.stdout).to eq "" expect(result.stdout).to eq ""
end end
it "raises an error when an unknown source file is specified" do
test_dir("cfile")
result = run_rscons(args: %w[-f error_unknown_extension.rb])
expect(result.stderr).to match /Unknown source file .foo.bar. for CFile builder/
expect(result.status).to_not eq 0
end
end end
context "Command builder" do context "Command builder" do