1
0
Fork 0
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:
Alberto García Hierro 2017-09-30 10:02:11 +01:00
parent 40f2a85439
commit 0a6328d8e8
4 changed files with 229 additions and 72 deletions

View file

@ -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
View 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
View 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

View file

@ -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