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