Class: Origen::FileHandler

Inherits:
Object show all
Defined in:
lib/origen/file_handler.rb

Overview

All logic for working with files/directories and resolving path names should be included here.

An instance of this class is available as Origen.file_handler

Some portions of Origen may implement local code to do this, but these should all be transitioned to use this over time.

Instance Attribute Summary collapse

Instance Method Summary collapse

Instance Attribute Details

#default_extensionObject

Returns the value of attribute default_extension



12
13
14
# File 'lib/origen/file_handler.rb', line 12

def default_extension
  @default_extension
end

Instance Method Details

#add_extension_to(file) ⇒ Object



304
305
306
307
308
309
310
311
312
313
314
315
# File 'lib/origen/file_handler.rb', line 304

def add_extension_to(file)
  f = Pathname.new(file)
  if f.basename('.erb').extname.empty?
    if default_extension
      "#{f.dirname}/#{f.basename('.erb')}#{default_extension}.erb"
    else
      "#{f.dirname}/#{f.basename('.erb')}#{Pathname.new(Origen.file_handler.current_file).basename('.erb').extname}.erb"
    end
  else
    "#{f.dirname}/#{f.basename('.erb')}.erb"
  end
end

#add_rb_to(file) ⇒ Object



299
300
301
302
# File 'lib/origen/file_handler.rb', line 299

def add_rb_to(file)
  f = Pathname.new(file)
  "#{f.dirname}/#{f.basename('.rb')}.rb"
end

#add_underscore_to(file) ⇒ Object

Insert _ in file name if not present



290
291
292
293
294
295
296
297
# File 'lib/origen/file_handler.rb', line 290

def add_underscore_to(file)
  f = Pathname.new(file)
  if f.basename.to_s =~ /^_/
    file
  else
    "#{f.dirname}/_#{f.basename}"
  end
end

#all_matches(file, options) ⇒ Object



169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
# File 'lib/origen/file_handler.rb', line 169

def all_matches(file, options)
  if Origen.app.plugins.current
    matches = Dir.glob("#{options[:default_dir]}/#{Origen.app.plugins.current.name}/**/#{file}").sort
    matches = matches.flatten.uniq
    if matches.size == 0
      matches = Dir.glob("#{options[:default_dir]}/**/#{file}").sort
      matches = matches.flatten.uniq
    end
  else
    matches = (Dir.glob("#{options[:default_dir]}/**/#{file}") + # Avoids symlinks
                Dir.glob("#{options[:default_dir]}/#{file}")).sort
    if matches.size == 0
      matches = Dir.glob("#{options[:default_dir]}/**{,/*/**}/#{file}").sort # Takes symlinks into consideration
    end
    matches = matches.flatten.uniq
  end

  if matches.size == 0
    return nil
  elsif matches.size > 1
    puts 'The following matches were found:'
    puts matches
    fail "Ambiguous file #{file}"
  else
    return check(matches.first)
  end
end

#base_directoryObject

Returns the base directory containing the source files being generated/compiled.

When operating on a single file this will return the directory containing that file, when operating on a directory this will return the directory.



368
369
370
# File 'lib/origen/file_handler.rb', line 368

def base_directory
  @base_directory
end

#base_directory=(file_or_dir) ⇒ Object



372
373
374
375
376
377
378
379
# File 'lib/origen/file_handler.rb', line 372

def base_directory=(file_or_dir)
  # puts "Base directory changed by: #{caller[0]}"
  if file_or_dir.directory?
    @base_directory = file_or_dir
  else
    @base_directory = file_or_dir.dirname
  end
end

#check(path) ⇒ Object



150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
# File 'lib/origen/file_handler.rb', line 150

def check(path)
  file_plugin = Origen.app.plugins.path_within_a_plugin(path)
  if file_plugin
    if Origen.app.plugins.current
      if file_plugin == Origen.app.plugins.current.name
        return path
      else
        puts "The requested file is from plugin #{file_plugin} and current system plugin is set to plugin #{Origen.app.plugins.current.name}!"
        fail 'Incorrect plugin error!'
      end
    else
      Origen.app.plugins.temporary = file_plugin
      return path
    end
  else
    return path
  end
end

#clean_path_to(file, options = {}) ⇒ Object

Returns a full path to the given file or directory, raises an error if it can't be resolved



100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
# File 'lib/origen/file_handler.rb', line 100

def clean_path_to(file, options = {})
  # Allow individual calls to this method to specify additional custom load paths to consider
  if options[:load_paths]
    Array(options[:load_paths]).each do |root|
      if File.exist?("#{root}/#{file}")
        return Pathname.new("#{root}/#{file}")
      end
    end
  end
  if File.exist?(file)
    if Pathname.new(file).absolute?
      Pathname.new(file)
    else
      Pathname.new("#{Pathname.pwd}/#{file}")
    end
  # Is it a relative reference within a list file?
  elsif @last_opened_list_dir && File.exist?("#{@last_opened_list_dir}/#{file}")
    Pathname.new("#{@last_opened_list_dir}/#{file}")
  # Is it a relative reference to the current base directory?
  elsif File.exist?("#{base_directory}/#{file}")
    Pathname.new("#{base_directory}/#{file}")
  # Is it a path relative to Origen.root?
  elsif File.exist?("#{Origen.root}/#{file}")
    Pathname.new("#{Origen.root}/#{file}")
  # Is it a path relative to the current directory?
  elsif current_directory && File.exist?("#{current_directory}/#{file}")
    Pathname.new("#{current_directory}/#{file}")
  # Is it a path relative to the current plugin's Origen.root?
  elsif Origen.app.plugins.current && File.exist?("#{Origen.app.plugins.current.root}/#{file}")
    Pathname.new("#{Origen.app.plugins.current.root}/#{file}")
  elsif options[:default_dir]
    m = all_matches(file, options)
    if m
      Pathname.new(m)
    else
      if options[:allow_missing]
        return nil
      else
        fail "Can't find: #{file}"
      end
    end
  else
    if options[:allow_missing]
      return nil
    else
      fail "Can't find: #{file}"
    end
  end
end

#clean_path_to_sub_program(file) ⇒ Object

Deprecated.


282
283
284
285
286
287
# File 'lib/origen/file_handler.rb', line 282

def clean_path_to_sub_program(file)
  Origen.deprecated 'Origen.file_handler.clean_path_to_sub_program is deprecated, update to use version ... of origen_testers instead.'
  file = add_underscore_to(file)
  file = add_rb_to(file)
  clean_path_to(file)
end

#clean_path_to_sub_template(file) ⇒ Object



210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
# File 'lib/origen/file_handler.rb', line 210

def clean_path_to_sub_template(file)
  if File.exist?(file)
    if Pathname.new(file).absolute?
      return Pathname.new(file)
    else
      return Pathname.new("#{Pathname.pwd}/#{file}")
    end
  end
  file = inject_import_path(file, type: :template)
  file = add_underscore_to(file)
  file = add_extension_to(file)
  web_file = file =~ /\.(html|md)(\.|$)/
  begin
    # Allow relative references to templates/web when compiling a web template
    if Origen.lsf.current_command == 'web' || web_file
      clean_path_to(file, load_paths: ["#{Origen.root}/app/templates/web", "#{Origen.root}/templates/web"])
    else
      clean_path_to(file)
    end
  rescue
    # Try again without .erb
    file = file.gsub('.erb', '')
    if Origen.lsf.current_command == 'web' || web_file
      clean_path_to(file, load_paths: ["#{Origen.root}/app/templates/web", "#{Origen.root}/templates/web"])
    else
      clean_path_to(file)
    end
  end
end

#clean_path_to_template(file) ⇒ Object



240
241
242
243
244
# File 'lib/origen/file_handler.rb', line 240

def clean_path_to_template(file)
  file = inject_import_path(file, type: :template)
  file = add_extension_to(file)
  clean_path_to(file)
end

#current_directoryObject



381
382
383
384
# File 'lib/origen/file_handler.rb', line 381

def current_directory
  return @current_directory if @current_directory
  @current_directory = clean_path_to(current_file).dirname if current_file
end

#current_fileObject



391
392
393
# File 'lib/origen/file_handler.rb', line 391

def current_file
  @current_file
end

#current_file=(file) ⇒ Object



386
387
388
389
# File 'lib/origen/file_handler.rb', line 386

def current_file=(file)
  @current_directory = nil
  @current_file = file
end

#expand_list(files, options = {}) ⇒ Object

Returns an array of file/pattern names lines from a list file. This will also take care of recursively expanding any embedded list references.



17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
# File 'lib/origen/file_handler.rb', line 17

def expand_list(files, options = {})
  options = {
    preserve_duplicates: tester && tester.try(:sim?)
  }.merge(options)
  list_of_files = [files].flatten.map do |file|
    f = file.strip
    # Takes care of blank or comment lines in a list file
    if f.empty? || f =~ /^\s*#/
      nil
    # Don't expand program lists when submitting to lsf,
    # there are likely to be relational dependencies between
    # flows meaning that they must be generated together
    elsif is_a_list?(f) && !(options[:lsf] && options[:action] == :program)
      expand_list(open_list(f), options)
    else
      f
    end
  end.flatten.compact
  if options[:preserve_duplicates]
    list_of_files
  else
    list_of_files.uniq
  end
end

#inject_import_path(path, options = {}) ⇒ Object

If the current path looks like it is a reference to an import, the path will be replaced with the absolute path to the local import directory



248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
# File 'lib/origen/file_handler.rb', line 248

def inject_import_path(path, options = {})
  path = path.to_s unless path.is_a?(String)
  if path =~ /(.*?)\/.*/
    import_name = Regexp.last_match[1].downcase.to_sym
    if import_name == :origen || import_name == :origen_core || Origen.app.plugins.names.include?(import_name) ||
       import_name == :doc_helpers
      # Special case to allow a shortcut for this common import plugin and to also handle legacy
      # code from when it was called doc_helpers instead of origen_doc_helpers
      if import_name == :doc_helpers
        root = Origen.app(:origen_doc_helpers).root
      else
        unless import_name == :origen || import_name == :origen_core
          root = Origen.app(import_name).root
        end
      end
      if options[:type] == :template
        if import_name == :origen || import_name == :origen_core
          path.sub! 'origen', "#{Origen.top}/templates/shared"
        else
          if File.exist?("#{root}/app/templates/shared")
            path.sub! Regexp.last_match[1], "#{root}/app/templates/shared"
          else
            path.sub! Regexp.last_match[1], "#{root}/templates/shared"
          end
        end
      else
        fail 'Unknown import path type!'
      end
    end
  end
  path
end

#is_a_list?(file) ⇒ Boolean

Returns true if the input argument is a list, for now this is simply defined by the filename ending in .list

Returns:

  • (Boolean)


66
67
68
# File 'lib/origen/file_handler.rb', line 66

def is_a_list?(file)
  !!(file =~ /list$/)
end

#open_for_write(path) ⇒ Object

Convenience method to use when you want to write to a file, this takes care of ensuring that the directory exists prior to attempting to open the file



449
450
451
452
453
454
455
# File 'lib/origen/file_handler.rb', line 449

def open_for_write(path)
  dir = Pathname.new(path).dirname
  FileUtils.mkdir_p(dir) unless File.exist?(dir)
  File.open(path, 'w') do |f|
    yield f
  end
end

#open_list(file) ⇒ Object

Returns the contents of the given list file in an array, if it can be found, if not will raise an error



44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
# File 'lib/origen/file_handler.rb', line 44

def open_list(file)
  f = clean_path_to(file, allow_missing: true)
  if f
    f = File.open(f, 'r')
  elsif File.exist?("#{Origen.root}/list/#{File.basename(file)}")
    f = File.open("#{Origen.root}/list/#{File.basename(file)}", 'r')
  elsif @last_opened_list_dir && File.exist?("#{@last_opened_list_dir}/#{file}")
    f = File.open("#{@last_opened_list_dir}/#{file}", 'r')
  else
    fail "Could not find list file: #{file}"
  end
  lines = f.readlines
  f.close
  # Before we go save the directory of this list, this will help
  # us to resolve any relative path references to other lists that
  # it may contain
  @last_opened_list_dir = clean_path_to(Pathname.new(f).dirname)
  lines
end

#output_directoryObject

Returns an absolute pathname to the current output directory



333
334
335
# File 'lib/origen/file_handler.rb', line 333

def output_directory
  @output_directory ||= set_output_directory
end

#preserve_and_clear_stateObject



415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
# File 'lib/origen/file_handler.rb', line 415

def preserve_and_clear_state
  file = current_file
  dir = base_directory
  output = output_directory
  ref = reference_directory
  ext = default_extension
  current_file = nil
  base_directory = nil
  output_directory = nil
  reference_directory = nil
  yield
  self.base_directory = dir if dir
  self.current_file = file if file
  set_output_directory(output: output) if output
  set_reference_directory(reference: ref) if ref
  self.default_extension = ext
end

#preserve_current_fileObject



395
396
397
398
399
# File 'lib/origen/file_handler.rb', line 395

def preserve_current_file
  file = current_file
  yield
  self.current_file = file
end

#preserve_stateObject



401
402
403
404
405
406
407
408
409
410
411
412
413
# File 'lib/origen/file_handler.rb', line 401

def preserve_state
  file = current_file
  dir = base_directory
  output = output_directory
  ref = reference_directory
  ext = default_extension
  yield
  self.base_directory = dir if dir
  self.current_file = file if file
  set_output_directory(output: output) if output
  set_reference_directory(reference: ref) if ref
  self.default_extension = ext
end

#reference_directoryObject

Returns an absolute pathname to the current reference directory



360
361
362
# File 'lib/origen/file_handler.rb', line 360

def reference_directory
  @reference_directory ||= set_reference_directory
end

#relative_path_to(path) ⇒ Object



206
207
208
# File 'lib/origen/file_handler.rb', line 206

def relative_path_to(path)
  clean_path_to(path).relative_path_from(Pathname.pwd)
end

#relative_to_absolute(path) ⇒ Object

Returns an absolute path for the given



198
199
200
201
202
203
204
# File 'lib/origen/file_handler.rb', line 198

def relative_to_absolute(path)
  if Pathname.new(path).absolute?
    Pathname.new(path)
  else
    Pathname.new("#{Pathname.pwd}/#{path}")
  end
end

#resolve_files(file_or_dir_path, options = {}, &block) ⇒ Object

Yields absolute paths to the given file or directory. If a directory is supplied the method will recurse into the sub directories and ultimately yield every file contained within the directory and its children.



73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
# File 'lib/origen/file_handler.rb', line 73

def resolve_files(file_or_dir_path, options = {}, &block)
  options = {
    # Set to :template when calling to consider references to template
    # files from an import library
    import: false
  }.merge(options)
  [file_or_dir_path].flatten.each do |file_or_dir_path|
    path = inject_import_path(file_or_dir_path, type: options[:import]) if options[:import]
    path = clean_path_to(file_or_dir_path, options)
    self.base_directory = path unless options[:internal_call]
    if path.directory?
      Dir.glob("#{path}/*").sort.each do |file|
        resolve_files(file, { internal_call: true }.merge(options), &block)
      end
    else
      # Ignore files with the given prefix if supplied, but only if this is a file that
      # has been found, if explicitly asked to compile a file from the caller do it regardless
      if options[:ignore_with_prefix] && options[:internal_call]
        return nil if path.basename.to_s =~ /^#{options[:ignore_with_prefix]}/
      end
      yield path
    end
  end
end

#set_output_directory(options = {}) ⇒ Object



317
318
319
320
321
322
323
324
325
326
327
328
329
330
# File 'lib/origen/file_handler.rb', line 317

def set_output_directory(options = {})
  options = {
    create: true
  }.merge(options)
  if options[:output]
    @output_directory = relative_to_absolute(options[:output])
  else
    @output_directory = Pathname.new(Origen.config.output_directory)
  end
  if options[:create]
    FileUtils.mkdir_p(@output_directory) unless @output_directory.exist?
  end
  @output_directory
end

#set_reference_directory(options = {}) ⇒ Object



337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
# File 'lib/origen/file_handler.rb', line 337

def set_reference_directory(options = {})
  options = {
    create: true
  }.merge(options)
  if options[:reference]
    @reference_directory = relative_to_absolute(options[:reference])
  else
    @reference_directory = Pathname.new(Origen.config.reference_directory)
    # Create the reference output directory if it does not exist.
    FileUtils.mkdir_p(@reference_directory) unless @reference_directory.exist?
  end
  if options[:create]
    # Delete any broken symlinks in the top level .ref
    dir = "#{Origen.root}/.ref"
    if File.symlink?(dir)
      FileUtils.rm_f(dir) unless File.exist?(dir)
    end
    FileUtils.mkdir_p(@reference_directory) unless @reference_directory.exist?
  end
  @reference_directory
end

#sub_dir_of(file, base = base_directory) ⇒ Object

Returns the sub directory of the current base directory that the given file is in



435
436
437
438
439
440
441
442
443
444
# File 'lib/origen/file_handler.rb', line 435

def sub_dir_of(file, base = base_directory)
  file = Pathname.new(file) unless file.respond_to?(:relative_path_from)
  base = Pathname.new(base) unless base.respond_to?(:relative_path_from)
  rel = file.relative_path_from(base)
  if file.directory?
    rel
  else
    rel.dirname
  end
end