mirror of
https://github.com/iNavFlight/inav.git
synced 2025-07-19 14:25:16 +03:00
Run the settings generator only when the build changes
A build is considered changed if either the TARGET, CFLAGS or the #define'd constants by including target.h and platform.h change The Makefile now runs a new tool in src/utils/build_stamp.rb which updates the file in obj/build.stamp iff the build has changed. This allows us to make the generated files to depend on this stamp file rather than rebuilding them all the time. Automatic dependencies generated by the compiler take care of recompiling just the files which depend on the generated ones.
This commit is contained in:
parent
40f2a85439
commit
0a6328d8e8
4 changed files with 229 additions and 72 deletions
26
Makefile
26
Makefile
|
@ -924,28 +924,29 @@ CLEAN_ARTIFACTS += $(TARGET_ELF) $(TARGET_OBJS) $(TARGET_MAP)
|
|||
$(OBJECT_DIR)/$(TARGET)/build/version.o : $(TARGET_SRC)
|
||||
|
||||
# Settings generator
|
||||
.PHONY: settings clean-settings
|
||||
UTILS_DIR = $(ROOT)/src/utils
|
||||
.PHONY: .FORCE clean-settings
|
||||
UTILS_DIR = $(ROOT)/src/utils
|
||||
SETTINGS_GENERATOR = $(UTILS_DIR)/settings.rb
|
||||
BUILD_STAMP = $(UTILS_DIR)/build_stamp.rb
|
||||
STAMP = $(BIN_DIR)/build.stamp
|
||||
|
||||
GENERATED_SETTINGS = $(SRC_DIR)/fc/settings_generated.h $(SRC_DIR)/fc/settings_generated.c
|
||||
SETTINGS_FILE = $(SRC_DIR)/fc/settings.yaml
|
||||
$(GENERATED_SETTINGS): $(SETTINGS_GENERATOR) $(SETTINGS_FILE)
|
||||
GENERATED_FILES = $(GENERATED_SETTINGS)
|
||||
$(GENERATED_SETTINGS): $(SETTINGS_GENERATOR) $(SETTINGS_FILE) $(STAMP)
|
||||
|
||||
$(STAMP): .FORCE
|
||||
$(V1) CFLAGS="$(CFLAGS)" TARGET=$(TARGET) ruby $(BUILD_STAMP) $(SETTINGS_FILE) $(STAMP)
|
||||
|
||||
# Use a pattern rule, since they're different than normal rules.
|
||||
# See https://www.gnu.org/software/make/manual/make.html#Pattern-Examples
|
||||
%generated.h %generated.c:
|
||||
$(V1) echo "settings.yaml -> settings_generated.h, settings_generated.c" "$(STDOUT)"
|
||||
$(V1) CFLAGS="$(CFLAGS)" ruby $(SETTINGS_GENERATOR) . $(SETTINGS_FILE)
|
||||
$(V1) CFLAGS="$(CFLAGS)" TARGET=$(TARGET) ruby $(SETTINGS_GENERATOR) . $(SETTINGS_FILE)
|
||||
|
||||
settings: $(GENERATED_SETTINGS)
|
||||
clean-settings:
|
||||
$(V1) $(RM) $(GENERATED_SETTINGS)
|
||||
|
||||
# Files that depend on the generated settings
|
||||
$(OBJECT_DIR)/$(TARGET)/fc/cli.o: settings
|
||||
$(OBJECT_DIR)/$(TARGET)/fc/settings.o: settings
|
||||
|
||||
# List of buildable ELF files and their object dependencies.
|
||||
# It would be nice to compute these lists, but that seems to be just beyond make.
|
||||
|
||||
|
@ -955,7 +956,7 @@ $(TARGET_HEX): $(TARGET_ELF)
|
|||
$(TARGET_BIN): $(TARGET_ELF)
|
||||
$(V0) $(OBJCOPY) -O binary $< $@
|
||||
|
||||
$(TARGET_ELF): clean-settings $(TARGET_OBJS)
|
||||
$(TARGET_ELF): $(TARGET_OBJS)
|
||||
$(V1) echo Linking $(TARGET)
|
||||
$(V1) $(CROSS_CC) -o $@ $(filter %.o, $^) $(LDFLAGS)
|
||||
$(V0) $(SIZE) $(TARGET_ELF)
|
||||
|
@ -1069,7 +1070,10 @@ test:
|
|||
$(V0) cd src/test && $(MAKE) test || true
|
||||
|
||||
# rebuild everything when makefile changes
|
||||
$(TARGET_OBJS) : Makefile
|
||||
# Make the generated files and the build stamp order only prerequisites,
|
||||
# so they will be generated before TARGET_OBJS but regenerating them
|
||||
# won't cause all TARGET_OBJS to be rebuilt.
|
||||
$(TARGET_OBJS) : Makefile | $(GENERATED_FILES) $(STAMP)
|
||||
|
||||
# include auto-generated dependencies
|
||||
-include $(TARGET_DEPS)
|
||||
|
|
88
src/utils/build_stamp.rb
Normal file
88
src/utils/build_stamp.rb
Normal file
|
@ -0,0 +1,88 @@
|
|||
#!/usr/bin/env ruby
|
||||
|
||||
# This file is part of INAV.
|
||||
#
|
||||
# author: Alberto Garcia Hierro <alberto@garciahierro.com>
|
||||
#
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
# You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
#
|
||||
# Alternatively, the contents of this file may be used under the terms
|
||||
# of the GNU General Public License Version 3, as described below:
|
||||
#
|
||||
# This file is free software: you may copy, redistribute and/or modify
|
||||
# it under the terms of the GNU General Public License as published by the
|
||||
# Free Software Foundation, either version 3 of the License, or (at your
|
||||
# option) any later version.
|
||||
#
|
||||
# This file is distributed in the hope that it will be useful, but
|
||||
# WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
|
||||
# Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see http://www.gnu.org/licenses/.
|
||||
|
||||
require 'fileutils'
|
||||
require 'stringio'
|
||||
|
||||
require_relative 'compiler'
|
||||
|
||||
class Stamper
|
||||
def initialize(settings_file, stamp_file)
|
||||
@settings_file = settings_file
|
||||
@stamp_file = stamp_file
|
||||
@stamp_dir = File.dirname(@stamp_file)
|
||||
@compiler = Compiler.new
|
||||
end
|
||||
|
||||
def stamp
|
||||
buf = StringIO.new
|
||||
buf << "CFLAGS = " << ENV["CFLAGS"] << "\n"
|
||||
buf << "TARGET = " << ENV["TARGET"] << "\n"
|
||||
buf << "CONFIG = " << hash_config() << "\n"
|
||||
|
||||
stamp = buf.string
|
||||
|
||||
old_stamp = nil
|
||||
if File.file?(@stamp_file)
|
||||
old_stamp = File.open(@stamp_file, 'rb') { |f| f.read }
|
||||
end
|
||||
if old_stamp != stamp
|
||||
File.open(@stamp_file, 'w') {|f| f.write(stamp)}
|
||||
end
|
||||
end
|
||||
|
||||
def hash_config
|
||||
buf = StringIO.new
|
||||
["target.h", "platform.h", "cstddef"].each do |h|
|
||||
buf << "#include \"#{h}\"\n"
|
||||
end
|
||||
FileUtils.mkdir_p @stamp_dir
|
||||
input = File.join(@stamp_dir, "stamp.cpp")
|
||||
File.open(input, 'w') {|file| file.write(buf.string)}
|
||||
output = File.join(@stamp_dir, "stamp")
|
||||
stdout, stderr = @compiler.run(input, output, ["-dM", "-E"])
|
||||
File.delete(input)
|
||||
File.delete(output)
|
||||
return Digest::SHA1.hexdigest(stdout)
|
||||
end
|
||||
end
|
||||
|
||||
def usage
|
||||
puts "Usage: ruby #{__FILE__} <settings_file> <stamp_file>"
|
||||
end
|
||||
|
||||
if __FILE__ == $0
|
||||
|
||||
settings_file = ARGV[0]
|
||||
stamp_file = ARGV[1]
|
||||
if settings_file.nil? || stamp_file.nil?
|
||||
usage()
|
||||
exit(1)
|
||||
end
|
||||
|
||||
stamper = Stamper.new(settings_file, stamp_file)
|
||||
stamper.stamp()
|
||||
end
|
96
src/utils/compiler.rb
Normal file
96
src/utils/compiler.rb
Normal file
|
@ -0,0 +1,96 @@
|
|||
#!/usr/bin/env ruby
|
||||
|
||||
# This file is part of INAV.
|
||||
#
|
||||
# author: Alberto Garcia Hierro <alberto@garciahierro.com>
|
||||
#
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
# You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
#
|
||||
# Alternatively, the contents of this file may be used under the terms
|
||||
# of the GNU General Public License Version 3, as described below:
|
||||
#
|
||||
# This file is free software: you may copy, redistribute and/or modify
|
||||
# it under the terms of the GNU General Public License as published by the
|
||||
# Free Software Foundation, either version 3 of the License, or (at your
|
||||
# option) any later version.
|
||||
#
|
||||
# This file is distributed in the hope that it will be useful, but
|
||||
# WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
|
||||
# Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see http://www.gnu.org/licenses/.
|
||||
|
||||
require 'digest'
|
||||
require 'open3'
|
||||
require 'rbconfig'
|
||||
require 'shellwords'
|
||||
|
||||
class Compiler
|
||||
def initialize
|
||||
# Look for the compiler in PATH manually, since there
|
||||
# are some issues with the built-in search by spawn()
|
||||
# on Windows if PATH contains spaces.
|
||||
dirs = (ENV["PATH"] || "").split(File::PATH_SEPARATOR)
|
||||
bin = "arm-none-eabi-g++"
|
||||
dirs.each do |dir|
|
||||
p = File.join(dir, bin)
|
||||
['', '.exe'].each do |suffix|
|
||||
f = p + suffix
|
||||
if File.executable?(f)
|
||||
if @verbose
|
||||
puts "Found #{bin} at #{f}"
|
||||
end
|
||||
@path = f
|
||||
return
|
||||
end
|
||||
end
|
||||
end
|
||||
raise "Could not find #{bin} in PATH, looked in #{dirs}"
|
||||
@verbose = ENV["V"] == "1"
|
||||
end
|
||||
|
||||
def default_args
|
||||
cflags = Shellwords.split(ENV["CFLAGS"] || "")
|
||||
args = [@path]
|
||||
cflags.each do |flag|
|
||||
# Don't generate temporary files
|
||||
if flag == "" || flag == "-MMD" || flag == "-MP" || flag.start_with?("-save-temps")
|
||||
next
|
||||
end
|
||||
if flag.start_with? "-std="
|
||||
flag = "-std=c++11"
|
||||
end
|
||||
if flag.start_with? "-D'"
|
||||
# Cleanup flag. Done by the shell when called from
|
||||
# it but we must do it ourselves becase we're not
|
||||
# calling the compiler via shell.
|
||||
flag = "-D" + flag[3..-2]
|
||||
end
|
||||
args << flag
|
||||
end
|
||||
return args
|
||||
end
|
||||
|
||||
def run(input, output, args = nil)
|
||||
all_args = default_args
|
||||
if args
|
||||
all_args.push(*args)
|
||||
end
|
||||
all_args << "-o" << output << input
|
||||
stdout, stderr = Open3.capture3(join_args(all_args))
|
||||
return stdout, stderr
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def join_args(args)
|
||||
if RbConfig::CONFIG['host_os'] =~ /mswin|mingw/
|
||||
return args.join(' ')
|
||||
end
|
||||
return Shellwords.join(args)
|
||||
end
|
||||
end
|
|
@ -25,14 +25,13 @@
|
|||
# along with this program. If not, see http://www.gnu.org/licenses/.
|
||||
|
||||
require 'fileutils'
|
||||
require 'open3'
|
||||
require 'rbconfig'
|
||||
require 'set'
|
||||
require 'shellwords'
|
||||
require 'stringio'
|
||||
require 'tmpdir'
|
||||
require 'yaml'
|
||||
|
||||
require_relative 'compiler'
|
||||
|
||||
DEBUG = false
|
||||
INFO = false
|
||||
|
||||
|
@ -264,26 +263,31 @@ class Generator
|
|||
@settings_file = settings_file
|
||||
@output_dir = File.dirname(settings_file)
|
||||
|
||||
@gpluspluspath = nil
|
||||
@compiler = Compiler.new
|
||||
|
||||
@count = 0
|
||||
@max_name_length = 0
|
||||
@tables = Hash.new
|
||||
@used_tables = Set.new
|
||||
@enabled_tables = Set.new
|
||||
end
|
||||
|
||||
@data = YAML.load_file(settings_file)
|
||||
def write_files
|
||||
|
||||
# Remove the old files first, if any
|
||||
[header_file, impl_file].each do |file|
|
||||
if File.file?(file)
|
||||
File.delete(file)
|
||||
end
|
||||
end
|
||||
|
||||
@data = YAML.load_file(@settings_file)
|
||||
|
||||
initialize_tables
|
||||
check_conditions
|
||||
sanitize_fields
|
||||
initialize_name_encoder
|
||||
initialize_value_encoder
|
||||
end
|
||||
|
||||
def write_files
|
||||
header_file = File.join(@output_dir, "settings_generated.h")
|
||||
impl_file = File.join(@output_dir, "settings_generated.c")
|
||||
|
||||
write_header_file(header_file)
|
||||
write_impl_file(impl_file)
|
||||
|
@ -312,8 +316,22 @@ class Generator
|
|||
|
||||
private
|
||||
|
||||
def header_file
|
||||
File.join(@output_dir, "settings_generated.h")
|
||||
end
|
||||
|
||||
def impl_file
|
||||
File.join(@output_dir, "settings_generated.c")
|
||||
end
|
||||
|
||||
def write_file_header(buf)
|
||||
buf << "// This file has been automatically generated by utils/settings.rb\n"
|
||||
buf << "// Don't make any modifications to it. They will be lost.\n\n"
|
||||
end
|
||||
|
||||
def write_header_file(file)
|
||||
buf = StringIO.new
|
||||
write_file_header(buf)
|
||||
buf << "#pragma once\n"
|
||||
# Write clivalue_t size constants
|
||||
buf << "#define CLIVALUE_MAX_NAME_LENGTH #{@max_name_length+1}\n" # +1 for the terminating '\0'
|
||||
|
@ -354,6 +372,7 @@ class Generator
|
|||
|
||||
def write_impl_file(file)
|
||||
buf = StringIO.new
|
||||
write_file_header(buf)
|
||||
add_header = ->(h) {
|
||||
buf << "#include \"#{h}\"\n"
|
||||
}
|
||||
|
@ -573,37 +592,7 @@ class Generator
|
|||
value
|
||||
end
|
||||
|
||||
def find_gplusplus
|
||||
# Look for the compiler in PATH manually, since there
|
||||
# are some issues with the built-in search by spawn()
|
||||
# on Windows if PATH contains spaces.
|
||||
return if @gpluspluspath
|
||||
dirs = (ENV["PATH"] || "").split(File::PATH_SEPARATOR)
|
||||
bin = "arm-none-eabi-g++"
|
||||
dirs.each do |dir|
|
||||
p = File.join(dir, bin)
|
||||
['', '.exe'].each do |suffix|
|
||||
f = p + suffix
|
||||
if File.executable?(f)
|
||||
dputs "Found #{bin} at #{f}"
|
||||
@gpluspluspath = f
|
||||
return
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
raise "Could not find #{bin} in PATH, looked in #{dirs}"
|
||||
end
|
||||
|
||||
def join_args(args)
|
||||
if RbConfig::CONFIG['host_os'] =~ /mswin|mingw/
|
||||
return args.join(' ')
|
||||
end
|
||||
return Shellwords.join(args)
|
||||
end
|
||||
|
||||
def compile_test_file(prog)
|
||||
find_gplusplus
|
||||
buf = StringIO.new
|
||||
# cstddef for offsetof()
|
||||
headers = ["target.h", "platform.h", "cstddef"]
|
||||
|
@ -623,28 +612,8 @@ class Generator
|
|||
mktmpdir do |dir|
|
||||
file = File.join(dir, "test.cpp")
|
||||
File.open(file, 'w') {|file| file.write(buf.string)}
|
||||
cflags = Shellwords.split(ENV["CFLAGS"] || "")
|
||||
args = [@gpluspluspath]
|
||||
cflags.each do |flag|
|
||||
# Don't generate temporary files
|
||||
if flag == "" || flag == "-MMD" || flag == "-MP" || flag.start_with?("-save-temps")
|
||||
next
|
||||
end
|
||||
if flag.start_with? "-std="
|
||||
flag = "-std=c++11"
|
||||
end
|
||||
if flag.start_with? "-D'"
|
||||
# Cleanup flag. Done by the shell when called from
|
||||
# it but we must do it ourselves becase we're not
|
||||
# calling the compiler via shell.
|
||||
flag = "-D" + flag[3..-2]
|
||||
end
|
||||
args << flag
|
||||
end
|
||||
|
||||
args << "-o" << File.join(dir, "test") << file
|
||||
dputs "Compiling #{buf.string}"
|
||||
stdout, stderr = Open3.capture3(join_args(args))
|
||||
stdout, stderr = @compiler.run(file, File.join(dir, "test"))
|
||||
dputs "Output: #{stderr}"
|
||||
stderr
|
||||
end
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue