| 1 | require 'rake' |
| 2 | require 'pathname' |
| 3 | require 'fileutils' |
| 4 | |
| 5 | verbose(ENV['verbose'] == '1') |
| 6 | DEBUG = ENV['debug'] == '1' |
| 7 | TESTING = ENV['testing'] == '1' |
| 8 | |
| 9 | def pop_path(path) |
| 10 | Pathname(path).each_filename.to_a[1..-1] |
| 11 | end |
| 12 | |
| 13 | def obj2src(fn, e) |
| 14 | File.join('src', pop_path(File.dirname(fn)), File.basename(fn).ext(e)) |
| 15 | end |
| 16 | |
| 17 | def is_windows? |
| 18 | (/cygwin|mswin|mingw|bccwin|wince|emx/ =~ RUBY_PLATFORM) != nil |
| 19 | end |
| 20 | |
| 21 | # Makefile .d file loader to be used with the import file loader. |
| 22 | # this emulates the -include $(DEPFILES) in a Makefile for the generated .d files |
| 23 | class DfileLoader |
| 24 | include Rake::DSL |
| 25 | |
| 26 | SPACE_MARK = "\0" |
| 27 | |
| 28 | # Load the makefile dependencies in +fn+. |
| 29 | def load(fn) |
| 30 | return if ! File.exists?(fn) |
| 31 | lines = File.read fn |
| 32 | lines.gsub!(/\\ /, SPACE_MARK) |
| 33 | lines.gsub!(/#[^\n]*\n/m, "") |
| 34 | lines.gsub!(/\\\n/, ' ') |
| 35 | lines.each_line do |line| |
| 36 | process_line(line) |
| 37 | end |
| 38 | end |
| 39 | |
| 40 | private |
| 41 | |
| 42 | # Process one logical line of makefile data. |
| 43 | def process_line(line) |
| 44 | file_tasks, args = line.split(':', 2) |
| 45 | return if args.nil? |
| 46 | dependents = args.split.map { |d| respace(d) } |
| 47 | file_tasks.scan(/\S+/) do |file_task| |
| 48 | file_task = respace(file_task) |
| 49 | file file_task => dependents |
| 50 | end |
| 51 | end |
| 52 | |
| 53 | def respace(str) |
| 54 | str.tr SPACE_MARK, ' ' |
| 55 | end |
| 56 | end |
| 57 | |
| 58 | # Install the handler |
| 59 | Rake.application.add_loader('d', DfileLoader.new) |
| 60 | |
| 61 | PROG = 'smoothie' |
| 62 | |
| 63 | DEVICE = 'LPC1768' |
| 64 | ARCHITECTURE = 'armv7-m' |
| 65 | |
| 66 | MBED_DIR = './mbed/drop' |
| 67 | |
| 68 | TOOLSBIN = './gcc-arm-none-eabi/bin/arm-none-eabi-' |
| 69 | CC = "#{TOOLSBIN}gcc" |
| 70 | CCPP = "#{TOOLSBIN}g++" |
| 71 | LD = "#{TOOLSBIN}g++" |
| 72 | OBJCOPY = "#{TOOLSBIN}objcopy" |
| 73 | SIZE = "#{TOOLSBIN}size" |
| 74 | |
| 75 | # include a defaults file if present |
| 76 | load 'rakefile.defaults' if File.exists?('rakefile.defaults') |
| 77 | if TESTING |
| 78 | BUILDTYPE= 'Testing' |
| 79 | |
| 80 | elsif DEBUG |
| 81 | BUILDTYPE= 'Debug' |
| 82 | ENABLE_DEBUG_MONITOR= '0' |
| 83 | end |
| 84 | |
| 85 | # Set build type |
| 86 | BUILDTYPE= ENV['BUILDTYPE'] || 'Checked' unless defined? BUILDTYPE |
| 87 | puts "#{BUILDTYPE} build" |
| 88 | |
| 89 | ENABLE_DEBUG_MONITOR = ENV['ENABLE_DEBUG_MONITOR'] || '0' unless defined? ENABLE_DEBUG_MONITOR |
| 90 | |
| 91 | # set default baud rate |
| 92 | DEFAULT_SERIAL_BAUD_RATE= ENV['BAUDRATE'] || '115200' unless defined? DEFAULT_SERIAL_BAUD_RATE |
| 93 | |
| 94 | # set to true to eliminate all the network code |
| 95 | unless defined? NONETWORK |
| 96 | NONETWORK= false || TESTING |
| 97 | end |
| 98 | |
| 99 | # list of modules to exclude, include directory it is in |
| 100 | # e.g for a CNC machine |
| 101 | #EXCLUDE_MODULES = %w(tools/touchprobe tools/laser tools/temperaturecontrol tools/extruder) |
| 102 | |
| 103 | # generate regex of modules to exclude and defines |
| 104 | exclude_defines, excludes = EXCLUDE_MODULES.collect { |e| [e.tr('/', '_').upcase, e.sub('/', '\/')] }.transpose |
| 105 | |
| 106 | # see if network is enabled |
| 107 | if ENV['NONETWORK'] || NONETWORK |
| 108 | nonetwork= true |
| 109 | excludes << '\/libs\/Network\/' |
| 110 | puts "Excluding Network code" |
| 111 | else |
| 112 | nonetwork= false |
| 113 | end |
| 114 | |
| 115 | # see if CNC build |
| 116 | if ENV['CNC'] || CNC |
| 117 | cnc= true |
| 118 | excludes << 'panel\/screens\/3dprinter' |
| 119 | puts "CNC build" |
| 120 | else |
| 121 | excludes << 'panel\/screens\/cnc' |
| 122 | cnc= false |
| 123 | end |
| 124 | |
| 125 | if TESTING |
| 126 | # add modules to be tested here |
| 127 | TESTMODULES= %w(tools/temperatureswitch) unless defined? EXCLUDE_MODULES |
| 128 | puts "Modules under test: #{TESTMODULES}" |
| 129 | excludes << %w(Kernel.cpp main.cpp) # we replace these with mock versions in testframework |
| 130 | |
| 131 | frameworkfiles= FileList['src/testframework/*.{c,cpp}', 'src/testframework/easyunit/*.{c,cpp}'] |
| 132 | extrafiles= FileList['src/modules/communication/SerialConsole.cpp', 'src/modules/communication/utils/Gcode.cpp', 'src/modules/robot/Conveyor.cpp', 'src/modules/robot/Block.cpp'] |
| 133 | testmodules= FileList['src/libs/**/*.{c,cpp}'].include(TESTMODULES.collect { |e| "src/modules/#{e}/**/*.{c,cpp}"}).include(TESTMODULES.collect { |e| "src/testframework/unittests/#{e}/*.{c,cpp}"}).exclude(/#{excludes.join('|')}/) |
| 134 | SRC = frameworkfiles + extrafiles + testmodules |
| 135 | else |
| 136 | excludes << %w(testframework) |
| 137 | SRC = FileList['src/**/*.{c,cpp}'].exclude(/#{excludes.join('|')}/) |
| 138 | puts "WARNING Excluding modules: #{EXCLUDE_MODULES.join(' ')}" unless exclude_defines.empty? |
| 139 | end |
| 140 | |
| 141 | OBJDIR = 'OBJ' |
| 142 | OBJ = SRC.collect { |fn| File.join(OBJDIR, pop_path(File.dirname(fn)), File.basename(fn).ext('o')) } + |
| 143 | %W(#{OBJDIR}/configdefault.o #{OBJDIR}/mbed_custom.o) |
| 144 | |
| 145 | # list of header dependency files generated by compiler |
| 146 | DEPFILES = OBJ.collect { |fn| File.join(File.dirname(fn), File.basename(fn).ext('d')) } |
| 147 | |
| 148 | # create destination directories |
| 149 | SRC.each do |s| |
| 150 | d= File.join(OBJDIR, pop_path(File.dirname(s))) |
| 151 | FileUtils.mkdir_p(d) unless Dir.exists?(d) |
| 152 | end |
| 153 | |
| 154 | INCLUDE_DIRS = [Dir.glob(['./src/**/', './mri/**/'])].flatten |
| 155 | MBED_INCLUDE_DIRS = %W(#{MBED_DIR}/ #{MBED_DIR}/LPC1768/) |
| 156 | |
| 157 | INCLUDE = (INCLUDE_DIRS+MBED_INCLUDE_DIRS).collect { |d| "-I#{d}" }.join(" ") |
| 158 | |
| 159 | if ENABLE_DEBUG_MONITOR == '1' |
| 160 | # Can add MRI_UART_BAUD=115200 to next line if GDB fails to connect to MRI. |
| 161 | # Tends to happen on some Linux distros but not Windows and OS X. |
| 162 | MRI_UART = 'MRI_UART_0' |
| 163 | MRI_BREAK_ON_INIT = 1 unless defined? MRI_BREAK_ON_INIT |
| 164 | else |
| 165 | MRI_UART = 'MRI_UART_0 MRI_UART_SHARE' |
| 166 | MRI_BREAK_ON_INIT = 0 unless defined? MRI_BREAK_ON_INIT |
| 167 | end |
| 168 | |
| 169 | # Configure MRI variables based on BUILD_TYPE build type variable. |
| 170 | case BUILDTYPE.downcase |
| 171 | when 'release' |
| 172 | OPTIMIZATION = 2 |
| 173 | MRI_ENABLE = 0 |
| 174 | MRI_SEMIHOST_STDIO = 0 unless defined? MRI_SEMIHOST_STDIO |
| 175 | when 'debug' |
| 176 | OPTIMIZATION = 0 |
| 177 | MRI_ENABLE = 1 |
| 178 | MRI_SEMIHOST_STDIO = 1 unless defined? MRI_SEMIHOST_STDIO |
| 179 | when 'checked' |
| 180 | OPTIMIZATION = 2 |
| 181 | MRI_ENABLE = 1 |
| 182 | MRI_SEMIHOST_STDIO = 1 unless defined? MRI_SEMIHOST_STDIO |
| 183 | when 'testing' |
| 184 | OPTIMIZATION = 0 |
| 185 | MRI_ENABLE = 1 |
| 186 | MRI_SEMIHOST_STDIO = 0 unless defined? MRI_SEMIHOST_STDIO |
| 187 | end |
| 188 | |
| 189 | MRI_ENABLE = 1 unless defined? MRI_ENABLE # set to 0 to disable MRI |
| 190 | MRI_LIB = MRI_ENABLE == 1 ? './mri/mri.ar' : '' |
| 191 | MBED_LIB = "#{MBED_DIR}/LPC1768/GCC_ARM/libmbed.a" |
| 192 | |
| 193 | SYS_LIBS = '-lstdc++_s -lsupc++_s -lm -lgcc -lc_s -lgcc -lc_s -lnosys' |
| 194 | LIBS = [MBED_LIB, SYS_LIBS, MRI_LIB].join(' ') |
| 195 | |
| 196 | MRI_DEFINES = %W(-DMRI_ENABLE=#{MRI_ENABLE} -DMRI_INIT_PARAMETERS='"#{MRI_UART}"' -DMRI_BREAK_ON_INIT=#{MRI_BREAK_ON_INIT} -DMRI_SEMIHOST_STDIO=#{MRI_SEMIHOST_STDIO}) |
| 197 | |
| 198 | defines = %w(-DCHECKSUM_USE_CPP -D__LPC17XX__ -DTARGET_LPC1768 -DWRITE_BUFFER_DISABLE=0 -DSTACK_SIZE=3072 -DCHECKSUM_USE_CPP) |
| 199 | defines += exclude_defines.collect{|d| "-DNO_#{d}"} |
| 200 | defines += MRI_DEFINES |
| 201 | defines << "-DDEFAULT_SERIAL_BAUD_RATE=#{DEFAULT_SERIAL_BAUD_RATE}" |
| 202 | defines << '-DDEBUG' if OPTIMIZATION == 0 |
| 203 | defines << '-DNONETWORK' if nonetwork |
| 204 | defines << '-DCNC' if cnc |
| 205 | |
| 206 | DEFINES= defines.join(' ') |
| 207 | |
| 208 | # Compiler flags used to enable creation of header dependencies. |
| 209 | DEPFLAGS = '-MMD ' |
| 210 | CFLAGS = DEPFLAGS + "-Wall -Wextra -Wno-unused-parameter -Wcast-align -Wpointer-arith -Wredundant-decls -Wcast-qual -Wcast-align -O#{OPTIMIZATION} -g3 -mcpu=cortex-m3 -mthumb -mthumb-interwork -ffunction-sections -fdata-sections -fno-delete-null-pointer-checks" |
| 211 | CPPFLAGS = CFLAGS + ' -fno-rtti -std=gnu++11 -fno-exceptions' |
| 212 | CXXFLAGS = CFLAGS + ' -fno-rtti -std=gnu++11 -fexceptions' # used for a .cxx file that needs to be compiled with exceptions |
| 213 | |
| 214 | MRI_WRAPS = MRI_ENABLE == 1 ? ',--wrap=_read,--wrap=_write,--wrap=semihost_connected' : '' |
| 215 | |
| 216 | # Linker script to be used. Indicates what code should be placed where in memory. |
| 217 | LSCRIPT = "#{MBED_DIR}/LPC1768/GCC_ARM/LPC1768.ld" |
| 218 | LDFLAGS = "-mcpu=cortex-m3 -mthumb -specs=./build/startfile.spec" + |
| 219 | " -Wl,-Map=#{OBJDIR}/smoothie.map,--cref,--gc-sections,--wrap=_isatty,--wrap=malloc,--wrap=realloc,--wrap=free" + |
| 220 | MRI_WRAPS + |
| 221 | " -T#{LSCRIPT}" + |
| 222 | " -u _scanf_float -u _printf_float" |
| 223 | |
| 224 | HTTPD_FSDATA = './src/libs/Network/uip/webserver/httpd-fsdata2.h' |
| 225 | |
| 226 | # tasks |
| 227 | |
| 228 | # generate the header dependencies if they exist |
| 229 | import(*DEPFILES) |
| 230 | |
| 231 | task :clean do |
| 232 | FileUtils.rm_rf(OBJDIR) |
| 233 | end |
| 234 | |
| 235 | task :realclean => [:clean] do |
| 236 | sh "cd ./mbed;make clean" |
| 237 | end |
| 238 | |
| 239 | desc "Build the built-in web pages" |
| 240 | task :webui => [HTTPD_FSDATA] |
| 241 | |
| 242 | task :default => [:build] |
| 243 | |
| 244 | task :build => [MBED_LIB, :version, "#{PROG}.bin", :size] |
| 245 | |
| 246 | task :version do |
| 247 | if is_windows? |
| 248 | VERSION = ' -D__GITVERSIONSTRING__=\"place-holder\"' |
| 249 | else |
| 250 | v1= `git symbolic-ref HEAD 2> /dev/null` |
| 251 | v2= `git log --pretty=format:%h -1` |
| 252 | VERSION = ' -D__GITVERSIONSTRING__=\"' + "#{v1[11..-1].chomp}-#{v2}".chomp + '\"' |
| 253 | FileUtils.touch './src/version.cpp' # we want it compiled everytime |
| 254 | end |
| 255 | end |
| 256 | |
| 257 | desc "Upload via DFU" |
| 258 | task :upload do |
| 259 | sh "dfu-util -R -d 1d50:6015 -D #{OBJDIR}/#{PROG}.bin" |
| 260 | end |
| 261 | |
| 262 | task :size do |
| 263 | sh "#{SIZE} #{OBJDIR}/#{PROG}.elf" |
| 264 | end |
| 265 | |
| 266 | # build internal web page |
| 267 | WEB_SOURCE_FILES= FileList['./src/libs/Network/uip/webserver/httpd-fs-src/**/*'] |
| 268 | WEBDIR = './src/libs/Network/uip/webserver/httpd-fs' |
| 269 | file HTTPD_FSDATA => WEB_SOURCE_FILES do |
| 270 | FileUtils.rm_rf WEBDIR |
| 271 | FileUtils.mkdir WEBDIR |
| 272 | FileUtils.cp WEB_SOURCE_FILES, WEBDIR |
| 273 | # TODO minify some files |
| 274 | # sh 'java -jar yuicompressor-2.4.8.jar file -o file' |
| 275 | sh 'cd ./src/libs/Network/uip/webserver; perl makefsdata.pl' |
| 276 | sh 'cd ./src; make' |
| 277 | end |
| 278 | |
| 279 | file MBED_LIB do |
| 280 | puts "Building Mbed Using Make" |
| 281 | sh "cd ./mbed;make all" |
| 282 | end |
| 283 | |
| 284 | file "#{OBJDIR}/mbed_custom.o" => ['./build/mbed_custom.cpp'] do |t| |
| 285 | puts "Compiling mbed_custom.cpp" |
| 286 | sh "#{CCPP} #{CPPFLAGS} #{INCLUDE} #{DEFINES} -c -o #{t.name} #{t.prerequisites[0]}" |
| 287 | end |
| 288 | |
| 289 | file "#{OBJDIR}/configdefault.o" => 'src/config.default' do |t| |
| 290 | sh "cd ./src; ../#{OBJCOPY} -I binary -O elf32-littlearm -B arm --readonly-text --rename-section .data=.rodata.configdefault config.default ../#{OBJDIR}/configdefault.o" |
| 291 | end |
| 292 | |
| 293 | file "#{PROG}.bin" => ["#{PROG}.elf"] do |
| 294 | sh "#{OBJCOPY} -O binary #{OBJDIR}/#{PROG}.elf #{OBJDIR}/#{PROG}.bin" |
| 295 | end |
| 296 | |
| 297 | file "#{PROG}.elf" => OBJ do |t| |
| 298 | puts "Linking" |
| 299 | sh "#{LD} #{LDFLAGS} #{OBJ} #{LIBS} -o #{OBJDIR}/#{t.name}" |
| 300 | end |
| 301 | |
| 302 | #arm-none-eabi-objcopy -R .stack -O ihex ../LPC1768/main.elf ../LPC1768/main.hex |
| 303 | #arm-none-eabi-objdump -d -f -M reg-names-std --demangle ../LPC1768/main.elf >../LPC1768/main.disasm |
| 304 | |
| 305 | rule '.o' => lambda{ |objfile| obj2src(objfile, 'cpp') } do |t| |
| 306 | puts "Compiling #{t.source}" |
| 307 | sh "#{CCPP} #{CPPFLAGS} #{INCLUDE} #{DEFINES} #{VERSION} -c -o #{t.name} #{t.source}" |
| 308 | end |
| 309 | |
| 310 | rule '.o' => lambda{ |objfile| obj2src(objfile, 'cxx') } do |t| |
| 311 | puts "Compiling #{t.source}" |
| 312 | sh "#{CCPP} #{CXXFLAGS} #{INCLUDE} #{DEFINES} #{VERSION} -c -o #{t.name} #{t.source}" |
| 313 | end |
| 314 | |
| 315 | rule '.o' => lambda{ |objfile| obj2src(objfile, 'c') } do |t| |
| 316 | puts "Compiling #{t.source}" |
| 317 | sh "#{CC} #{CFLAGS} #{INCLUDE} #{DEFINES} #{VERSION} -c -o #{t.name} #{t.source}" |
| 318 | end |
| 319 | |