212 lines
7.2 KiB
Ruby
212 lines
7.2 KiB
Ruby
require 'rake/clean'
|
|
|
|
# =================
|
|
# project variables
|
|
# =================
|
|
|
|
# main name used if filename
|
|
target = "led-controller"
|
|
# schema
|
|
sch = "#{target}.sch"
|
|
# pcb layout
|
|
pcb = "#{target}.pcb"
|
|
# project version, read from "version" file
|
|
version = IO.read("version").chomp
|
|
# current date for stamping output
|
|
date = Time.now.strftime("%Y-%m-%d")
|
|
# schematic revision, based on the number of schematic commits)
|
|
sch_rev = `git log --pretty=oneline "#{sch}" | wc -l`.chomp.to_i
|
|
# pcb layout revision, based on the number of pcb commits)
|
|
pcb_rev = `git log --pretty=oneline "#{pcb}" | wc -l`.chomp.to_i
|
|
# schema name with version and revition
|
|
vsch = "#{target}_v#{version}.#{sch_rev.to_s.rjust(3,'0')}.sch"
|
|
# pcb layout name with version and revition
|
|
vpcb = "#{target}_v#{version}.#{pcb_rev.to_s.rjust(3,'0')}.pcb"
|
|
|
|
# ==========
|
|
# main tasks
|
|
# ==========
|
|
|
|
desc "main building task"
|
|
task :default => [:version,:print,:notes,:photo,:gerber]
|
|
|
|
desc "create release file"
|
|
task :release => "hardware-release_v#{version}.tar.gz"
|
|
CLOBBER.include("hardware-release_v#{version}.tar.gz")
|
|
|
|
desc "set version in schematic and layout"
|
|
task :version => [vsch,vpcb]
|
|
CLEAN.include(vsch)
|
|
CLEAN.include(vpcb)
|
|
CLOBBER.include("#{target}_*.sch")
|
|
CLOBBER.include("#{target}_*.pcb")
|
|
|
|
desc "print schematic and layout (as pdf)"
|
|
task :print => ["#{target}_schematic.pdf","#{target}_layout.pdf"]
|
|
CLEAN.include("#{target}_schematic.pdf")
|
|
CLEAN.include("#{target}_layout.pdf")
|
|
|
|
desc "export notes from schematic"
|
|
task :notes => "notes.txt"
|
|
CLOBBER.include("notes.txt")
|
|
|
|
desc "verify schematic attributes"
|
|
task :verify => vsch do |t|
|
|
["value","footprint"].each do |attribute|
|
|
bom2(t.prerequisites[0],attribute).each do |data|
|
|
next unless data[attribute]=="unknown"
|
|
puts "#{attribute}s not defined for #{data[:refdes]*','}"
|
|
end
|
|
end
|
|
uniq = true
|
|
numbered = true
|
|
bom2(t.prerequisites[0],"refdes").each do |data|
|
|
uniq &= data[:refdes].size==1
|
|
numbered &= !data["refdes"].include?("?")
|
|
end
|
|
puts "not all refdes uniq" unless uniq
|
|
puts "not all refdes numbered" unless numbered
|
|
end
|
|
|
|
PHOTOS = ["#{target}_layout-top.png","#{target}_layout-bottom.png"]
|
|
desc "render layout"
|
|
task :photo => PHOTOS
|
|
CLOBBER.include(PHOTOS)
|
|
|
|
GERBERS = [".top.gbr",".topmask.gbr",".toppaste.gbr",".topsilk.gbr",".bottom.gbr",".bottommask.gbr",".bottompaste.gbr",".bottomsilk.gbr",".outline.gbr",".fab.gbr",".plated-drill.cnc",".unplated-drill.cnc"].collect{|suffix| target+suffix}
|
|
desc "export gerber"
|
|
task :gerber => GERBERS
|
|
CLOBBER.include(GERBERS)
|
|
|
|
desc "reformat gerber and drill output (some programs like LPKF CircuitPro have difficulties with gEDA pcb output)"
|
|
task :reformat => GERBERS do
|
|
GERBERS.each do |gerber|
|
|
if gerber.end_with? ".gbr" then
|
|
sh "gerbv --export=rs274x --output=#{gerber} #{gerber}"
|
|
elsif gerber.end_with? ".cnc" then
|
|
sh "gerbv --export=drill --output=#{gerber} #{gerber}"
|
|
end
|
|
end
|
|
end
|
|
|
|
# ================
|
|
# helper functions
|
|
# ================
|
|
|
|
# generate gnetlist bom2 and parse them
|
|
# arguments: schematic=schematic to usse, attributes=the attributs to use for generating bom2
|
|
# return [{:refdes => [refdes of component], <attributes> => text of attributes}]
|
|
def bom2(schematic, attributes)
|
|
to_return = []
|
|
# force attributes to be an array
|
|
attributes = case attributes.class
|
|
when String
|
|
[attributes]
|
|
when Array
|
|
attributes
|
|
else
|
|
[attributes.to_s]
|
|
end
|
|
# create gnetlist backend
|
|
File.open("attribs","w") do |attribs|
|
|
attributes.each do |attribute|
|
|
attribs.puts attribute
|
|
end
|
|
end
|
|
# generate bom2
|
|
list = `gnetlist -g bom2 -q -o - #{schematic}`
|
|
# parse bom2
|
|
regex = /^(?<refdes>(\w+,?)+):(?<attributes>.*):(?<qty>\d+)$/
|
|
list.each_line do |line|
|
|
next unless line =~ regex
|
|
data = line.match regex
|
|
hash = {refdes: data[:refdes].split(',')}
|
|
attributes.each_index do |i|
|
|
hash.merge!({attributes[i] => data[:attributes].split(':')[i]})
|
|
end
|
|
to_return << hash
|
|
end
|
|
return to_return
|
|
end
|
|
CLEAN.include("attribs")
|
|
|
|
# ===============
|
|
# file generation
|
|
# ===============
|
|
|
|
desc "copy schematic to version it: include version, revision, and date"
|
|
file vsch => sch do |t|
|
|
sh "cp #{t.prerequisites.join(' ')} #{t.name}"
|
|
# on \ is to prevent ruby interpreting it, th other is for sed
|
|
# the version
|
|
sh "sed -i 's/\\(version=\\)\\$Version\\$/\\1#{version}/' #{t.name}"
|
|
# the date
|
|
sh "sed -i 's/\\(date=\\)\\$Date\\$/\\1#{date}/' #{t.name}"
|
|
# the revision
|
|
sh "sed -i 's/\\(revision=\\)\\$Revision\\$/\\1#{sch_rev}/' #{t.name}"
|
|
end
|
|
|
|
desc "copy layout to version it: include version, date, and run teardrops when available"
|
|
file vpcb => pcb do |t|
|
|
sh "cp #{t.prerequisites.join(' ')} #{t.name}"
|
|
# on \ is to prevent ruby interpreting it, th other is for sed
|
|
# the version and revision
|
|
version_revision = "v#{version}.#{pcb_rev.to_s.rjust(3,'0')}"
|
|
sh "sed -i 's/\\$version\\$/#{version_revision}/' #{t.name}"
|
|
# the date
|
|
sh "sed -i 's/\\$date\\$/#{date}/' #{t.name}"
|
|
# run teardrop for vias and pins
|
|
if File.exist? "#{Dir.home}/.pcb/plugins/teardrops.so" then
|
|
sh "pcb --action-string \"djopt(splitlines) Teardrops() s() q()\" #{t.name}"
|
|
end
|
|
end
|
|
|
|
desc "generate printable version (PDF) of schematic"
|
|
file "#{target}_schematic.pdf" => vsch do |t|
|
|
sh "gaf export -f pdf -c -o #{t.name} #{t.prerequisites.join(' ')} 2> /dev/null"
|
|
end
|
|
|
|
desc "generate printable documentation (PDF) from layout"
|
|
file "#{target}_layout.pdf" => vpcb do |t|
|
|
sh "pcb -x ps --psfile #{t.name}.ps #{t.prerequisites.join(' ')} 2> /dev/null"
|
|
sh "ps2pdf #{t.name}.ps #{t.name} 2> /dev/null"
|
|
sh "rm #{t.name}.ps 2> /dev/null"
|
|
end
|
|
|
|
desc "generate note file from schematic, listing the 'note' attributes from elements"
|
|
file "notes.txt" => vsch do |t|
|
|
notes_data = bom2(t.prerequisites[0],"note")
|
|
File.open(t.name,"w") do |notes_file|
|
|
notes_data.each do |note|
|
|
next if note["note"]=="unknown"
|
|
notes_file.puts "#{note[:refdes]*','}:\n#{note["note"]}\n\n"
|
|
end
|
|
end
|
|
end
|
|
|
|
desc "generate photo realistic picture from layout (front side)"
|
|
file "#{target}_layout-top.png" => vpcb do |t|
|
|
# sh "pcb -x png --dpi 600 --format PNG --photo-mode --outfile #{target}_layout-top.png #{t.prerequisites.join(' ')}"
|
|
sh "pcb -x png --format PNG --photo-mode --outfile #{target}_layout-top.png #{t.prerequisites.join(' ')}"
|
|
end
|
|
|
|
desc "generate photo realistic picture from layout (bottom side)"
|
|
file "#{target}_layout-bottom.png" => vpcb do |t|
|
|
# sh "pcb -x png --dpi 600 --format PNG --photo-mode --photo-flip-x --outfile #{target}_layout-bottom.png #{t.prerequisites.join(' ')}"
|
|
sh "pcb -x png --format PNG --photo-mode --photo-flip-x --outfile #{target}_layout-bottom.png #{t.prerequisites.join(' ')}"
|
|
end
|
|
|
|
desc "export gerber (and drill) files from layout"
|
|
GERBERS.each do |gerber|
|
|
file gerber => vpcb do |t|
|
|
puts "make #{t.name}"
|
|
sh "pcb -x gerber --gerberfile #{target} --all-layers #{t.prerequisites.join(' ')}"
|
|
end
|
|
end
|
|
|
|
ATTACHMENTS = ["cern_ohl_v_1_2_howto.pdf","CHANGES.txt","LICENSE.txt","PRODUCT.txt"]
|
|
desc "create archive with release files"
|
|
file "hardware-release_v#{version}.tar.gz" => ATTACHMENTS+["lib",vsch,vpcb,"notes.txt","#{target}_schematic.pdf","#{target}_layout.pdf","#{target}_layout-top.png","#{target}_layout-bottom.png"]+GERBERS do |t|
|
|
sh "tar -acf '#{t.name}' #{t.prerequisites.join(' ')}"
|
|
end
|