class Drupid::Makefile
Representation of a Drush
makefile.
See also: drupal.org/node/625094
Attributes
The value of the api field of the makefile (e.g., ‘2’)
The path for contrib modules and themes (e.g., ‘sites/all’), relative to path
.
The value of the core field of the makefile (e.g, ‘7.x’)
The absolute path to the makefile.
Public Class Methods
Creates a new Makefile
object. The path must be the path to a .make file (which does not need to exist).
# File lib/drupid/makefile.rb 45 def initialize(path) 46 @path = Pathname.new(path) 47 raise "Not an absolute path: #{@path}" unless @path.absolute? 48 @core = nil 49 @api = nil 50 @projects = Hash.new # (String -> Project) 51 @libraries = Hash.new # (String -> Library) 52 @contrib_path = Pathname.new('sites/all') 53 debug "Parsing #{@path}" 54 self.reload if @path.exist? 55 end
Public Instance Methods
Adds a project to this specification.
# File lib/drupid/makefile.rb 278 def add_project(p) 279 @projects[p.name] = p 280 end
Removes the project with the specified name from this specification.
# File lib/drupid/makefile.rb 295 def delete_project(name) 296 @projects.delete(name) 297 end
Returns a Drupid::Project
object for the Drupal core specified in the makefile, or nil if the makefile does not specify a Drupal distribution.
# File lib/drupid/makefile.rb 310 def drupal_project 311 @projects['drupal'] 312 end
Iterates over the libraries in this specification.
# File lib/drupid/makefile.rb 315 def each_library 316 # For convenience, return the libraries in lexicographical order. 317 names = @libraries.keys.sort! 318 names.each do |n| 319 yield @libraries[n] 320 end 321 end
Iterates over the projects in this specification (excluding drupal).
# File lib/drupid/makefile.rb 300 def each_project 301 # For convenience, return the projects in lexicographical order. 302 names = @projects.keys.sort! 303 names.each do |n| 304 yield @projects[n] unless @projects[n].drupal? 305 end 306 end
Returns the library with the specified name. or nil if the library is not in this specification.
# File lib/drupid/makefile.rb 290 def get_library(name) 291 @libraries[name] 292 end
Returns the project with the specified name, or nil if the project is not in this specification.
# File lib/drupid/makefile.rb 284 def get_project(name) 285 @projects[name] 286 end
Returns a list of the names of the libraries mentioned in this specification.
# File lib/drupid/makefile.rb 331 def library_names 332 @libraries.keys 333 end
Returns a list of the names of the projects mentioned in this specification (excluding drupal).
# File lib/drupid/makefile.rb 325 def project_names 326 @projects.values.reject { |p| p.drupal? }.map { |p| p.name } 327 end
Reloads the makefile. This method is invoked automatically at creation time if a path to an existing makefile is provided.
# File lib/drupid/makefile.rb 60 def reload 61 @core = nil 62 @api = nil 63 @projects = Hash.new 64 @libraries = Hash.new 65 66 proj_patches = Hash.new 67 libs_patches = Hash.new 68 core_num = nil 69 mf = File.open(@path.to_s, "r").read 70 # Parse includes directives 71 while mf.match(/^([ \t]*includes\[.*\]\s*=\s*"?([^\s"]+)"?[ \t]*)$/) do 72 # TODO: add support for remote includes 73 url = $2 74 blah "Including makefile #{url}" 75 inc = File.open(url, "r").read 76 mf.sub!($1, inc) 77 end 78 if mf.match(/core *= *["']? *(\d+)\.?(\d+)?/) # Get the core number immediately 79 @core = $~[1] + '.x' 80 core_num = $~[1].to_i 81 vers = $~[2] ? $~[1] + '.' + $~[2] : nil 82 # Create Drupal project 83 @projects['drupal'] = Project.new('drupal', core_num, vers) 84 end 85 raise ParseMakefileError, "The makefile does not contain the mandatory 'core' field" unless core_num 86 lineno = 0 87 mf.each_line do |line| 88 lineno += 1 89 next if line =~ /^\s*$/ 90 next if line =~ /^\s*;/ 91 next if line =~ /^\s*core/ 92 # match[1] : the key ('core', 'version', 'api', 'projects', 'libraries', 'includes') 93 # match[2] : the (optional) key arguments (stuff between square brackets) 94 # match[3] : the same as match[2], but without the leftmost [ and the rightmost ] 95 # match[4] : the value 96 # Examples: 97 # (a) Given 'projects[ctools][version] = 1.0-rc1', we have 98 # match[1] == 'projects' 99 # match[2] == '[ctools][version]' 100 # match[3] == 'ctools][version' 101 # match[4] == '1.0-rc1' 102 # (b) Given 'core = 7.x', we have: 103 # match[1] == 'core' 104 # match[3] == nil 105 # match[4] == '7.x' 106 match = line.match(/^\s*([^\s\[=]+)\s*(\[\s*(.*?)\s*\])?\s*=\s*["']?([^\s"'(]+)/) 107 raise ParseMakefileError, "Could not parse line: #{line.strip} (line #{lineno})" if match.nil? or match.size != 5 108 key = match[1] 109 args = (match[3]) ? match[3].split(/\]\s*\[/) : [] 110 value = match[4].strip 111 case key 112 when 'api' 113 @api = value 114 when 'projects' 115 if 0 == args.size # e.g., projects[] = views 116 name = value 117 @projects[name] = Project.new(name, core_num) 118 else 119 name = args[0] 120 @projects[name] = Project.new(name, core_num) unless @projects.has_key?(name) 121 case args.size 122 when 1 # e.g., projects[views] = 2.8 123 @projects[name].version = @core+'-'+value.sub(/^#{@core}-/,'') 124 when 2 # e.g., projects[views][version] = 2.8 or projects[calendar][patch][] = 'http://...' 125 case args[1] 126 when 'version' 127 @projects[name].version = @core+'-'+value.sub(/^#{@core}-/,'') 128 when 'patch' 129 patch_key = File.basename(value) 130 patch_url = _normalize_path(value) 131 @projects[name].add_patch(patch_url, patch_key) 132 when 'subdir' 133 @projects[name].subdir = value 134 when 'location' 135 @projects[name].location = _normalize_path(value) 136 when 'directory_name' 137 @projects[name].directory_name = value 138 when 'type' 139 if 'core' == value 140 @projects[name].core_project = true 141 else 142 raise ParseMakefileError, "Illegal value: #{args[1]} (line #{lineno})" unless value =~ /^(module|profile|theme)$/ 143 @projects[name].proj_type = value 144 end 145 when 'l10n_path' 146 # TODO: add support for tokens 147 @projects[name].l10n_path = _normalize_path(value) 148 when 'l10n_url' 149 @projects[name].l10n_url = _normalize_path(value) 150 when 'overwrite' 151 @projects[name].overwrite = true if value =~ /TRUE/i 152 else 153 raise ParseMakefileError, "Unknown key: #{args[1]} (line #{lineno})" 154 end 155 when 3 # e.g., projects[mytheme][download][type] = "svn" 156 name = args[0] 157 subkey = args[1] 158 case subkey 159 when 'download' 160 case args[2] 161 when 'type' 162 @projects[name].download_type = value 163 when 'url' 164 @projects[name].download_url = _normalize_path(value) 165 else 166 @projects[name].add_download_spec(args[2], value) 167 end 168 else 169 raise ParseMakefileError, "Unknown key: #{subkey} (line #{lineno})" 170 end 171 when 4 # e.g., projects[calendar][patch][rfc-fixes][md5] = "..." 172 name = args[0] 173 subkey = args[1] 174 case subkey 175 when 'patch' 176 patch_key = args[2] 177 proj_patches[name] ||= Hash.new 178 proj_patches[name][patch_key] ||= Hash.new 179 case args[3] 180 when 'url' 181 proj_patches[name][patch_key]['url'] = _normalize_path(value) 182 when 'md5' 183 proj_patches[name][patch_key]['md5'] = value 184 else 185 raise ParseMakefileError, "Unknown key: #{subkey} (line #{lineno})" 186 end 187 else 188 raise ParseMakefileError, "Unknown key: #{subkey} (line #{lineno})" 189 end 190 else # > 4 arguments 191 raise ParseMakefileError, "Too many arguments (line #{lineno})" 192 end # case 193 end # if 194 when 'libraries' 195 if 0 == args.size 196 raise ParseMakefileError, "Too few arguments (line #{lineno})" 197 else 198 name = args[0] 199 @libraries[name] = Library.new(name) unless @libraries.has_key?(name) 200 case args.size 201 when 1 202 raise ParseMakefileError, "Too few arguments (line #{lineno})" 203 when 2 204 case args[1] 205 when 'patch' 206 patch_key = File.basename(value) 207 patch_url = _normalize_path(value) 208 @libraries[name].add_patch(patch_url, patch_key) 209 when 'subdir' 210 @libraries[name].subdir = value 211 when 'destination' 212 @libraries[name].destination = value 213 when 'directory_name' 214 @libraries[name].directory_name = value 215 else 216 raise ParseMakefileError, "Unknown key: #{args[1]} (line #{lineno})" 217 end 218 when 3 # e.g., libraries[jquery_ui][download][type] = "file" 219 name = args[0] 220 subkey = args[1] 221 case subkey 222 when 'download' 223 case args[2] 224 when 'type' 225 @libraries[name].download_type = value 226 when 'url' 227 @libraries[name].download_url = _normalize_path(value) 228 else 229 @libraries[name].add_download_spec(args[2], value) 230 end 231 else 232 raise ParseMakefileError, "Unknown key: #{subkey} (line #{lineno})" 233 end 234 when 4 235 name = args[0] 236 subkey = args[1] 237 case subkey 238 when 'patch' 239 patch_key = args[2] 240 libs_patches[name] ||= Hash.new 241 libs_patches[name][patch_key] ||= Hash.new 242 case args[3] 243 when 'url' 244 libs_patches[name][patch_key]['url'] = _normalize_path(value) 245 when 'md5' 246 libs_patches[name][patch_key]['md5'] = value 247 else 248 raise ParseMakefileError, "Unknown key: #{subkey} (line #{lineno})" 249 end 250 else 251 raise ParseMakefileError, "Unknown key: #{subkey} (line #{lineno})" 252 end 253 else # > 4 arguments 254 raise ParseMakefileError, "Too many arguments (line #{lineno})" 255 end 256 end 257 when 'includes' 258 owarn "Unexpected 'includes' directive (line #{lineno})" 259 else 260 owarn "Could not parse key: #{key} (line #{lineno})" 261 end 262 end 263 # Add missing patches 264 proj_patches.each do |proj_name, v| 265 v.each do |desc,prop| 266 @projects[proj_name].add_patch(prop['url'], desc, prop['md5']) 267 end 268 end 269 libs_patches.each do |lib_name, v| 270 v.each do |desc,prop| 271 @libraries[lib_name].add_patch(prop['url'], desc, prop['md5']) 272 end 273 end 274 return self 275 end
Writes this makefile to disk. An alternative location may be specified as an argument.
# File lib/drupid/makefile.rb 337 def save(alt_path = @path) 338 File.open(alt_path.to_s, "w").write(to_s) 339 end
Returns this makefile as a string.
# File lib/drupid/makefile.rb 342 def to_s 343 s = String.new 344 s << "core = #{@core}\n" 345 s << "api = #{@api}\n" 346 s << _project_to_record(drupal_project) if drupal_project 347 s << "\n" unless @projects.empty? 348 self.each_project { |p| s << _project_to_record(p) } 349 s << "\n" unless @libraries.empty? 350 self.each_library { |l| s << _library_to_record(l) } 351 s 352 end
Private Instance Methods
# File lib/drupid/makefile.rb 398 def _library_to_record(l) 399 fields = Array.new 400 fields << "[download][type] = \"#{l.download_type}\"" if l.download_type 401 fields << "[download][url] = \"#{_relativize_path(l.download_url)}\"" if l.download_url 402 temp = [] 403 l.download_specs.each do |spec,ref| 404 temp << "[download][#{spec}] = \"#{ref}\"" 405 end 406 fields = fields + temp.sort! 407 l.each_patch do |pa| 408 fields << "[patch][#{pa.descr}][url] = \"#{pa.url}\"" 409 fields << "[patch][#{pa.descr}][md5] = \"#{pa.md5}\"" if pa.md5 410 end 411 fields << "[destination] = \"#{l.destination}\"" 412 fields << "[subdir] = \"#{l.subdir}\"" if '.' != l.subdir.to_s 413 fields << "[directory_name] = \"#{l.directory_name}\"" 414 s = '' 415 fields.each do |f| 416 s << "libraries[#{l.name}]" + f + "\n" 417 end 418 return s 419 end
# File lib/drupid/makefile.rb 356 def _normalize_path(u) 357 return u if u =~ /:\/\// # URL 358 if u =~ /^\// # Local absolute path 359 return 'file://' + u 360 else # Relative path 361 return 'file://' + (path.parent + u).to_s 362 end 363 end
# File lib/drupid/makefile.rb 370 def _project_to_record(p) 371 fields = Array.new 372 fields << "[type] = \"#{p.proj_type}\"" if p.proj_type =~ /module|profile|theme/ 373 fields << "[version] = \"#{p.version.short}\"" if p.has_version? 374 fields << "[location] = \"#{_relativize_path(p.location)}\"" if p.location 375 fields << "[download][type] = \"#{p.download_type}\"" if p.download_type 376 fields << "[download][url] = \"#{_relativize_path(p.download_url)}\"" if p.download_url 377 temp = [] 378 p.download_specs.each do |spec,ref| 379 temp << "[download][#{spec}] = \"#{ref}\"" 380 end 381 fields = fields + temp.sort! 382 p.each_patch do |pa| 383 fields << "[patch][#{pa.descr}][url] = \"#{_relativize_path(pa.url)}\"" 384 fields << "[patch][#{pa.descr}][md5] = \"#{pa.md5}\"" if pa.md5 385 end 386 fields << "[l10n_path] = \"#{_relativize_path(p.l10n_path)}\"" if p.l10n_path 387 fields << "[l10n_url] = \"#{_relativize_path(p.l10n_url)}\"" if p.l10n_url 388 fields << "[subdir] = \"#{p.subdir}\"" if '.' != p.subdir.to_s 389 fields << "[directory_name] = \"#{p.directory_name}\"" if p.directory_name != p.name 390 return "projects[] = \"#{p.name}\"\n" if 0 == fields.size 391 s = '' 392 fields.each do |f| 393 s << "projects[#{p.name}]" + f + "\n" 394 end 395 return s 396 end
# File lib/drupid/makefile.rb 365 def _relativize_path(u) 366 return u unless u =~ /^file:\/\// 367 return Pathname.new(u.sub(/file:\/\//,'')).relative_path_from(path.dirname).to_s 368 end