Class: Udb::Resolver

Inherits:
Object
  • Object
show all
Extended by:
T::Sig
Defined in:
lib/udb/resolver.rb

Overview

resolves the specification in the context of a config, and writes to a generation folder

The primary interface for users will be #cfg_arch_for

Defined Under Namespace

Classes: ConfigInfo

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(repo_root = Udb.repo_root, schemas_path_override: nil, cfgs_path_override: nil, gen_path_override: nil, std_path_override: nil, custom_path_override: nil, python_path_override: nil)

create a new resolver.

With no arguments, resolver will assume it exists in the riscv-unified-db repository and use standard paths

If repo_root is given, use it as the path to a riscv-unified-db repository

Any specific path can be overridden. If all paths are overridden, it doesn’t matter what repo_root is.

Parameters:

  • repo_root (Pathname) (defaults to: Udb.repo_root)
  • schemas_path_override (Pathname, nil) (defaults to: nil)
  • cfgs_path_override (Pathname, nil) (defaults to: nil)
  • gen_path_override (Pathname, nil) (defaults to: nil)
  • std_path_override (Pathname, nil) (defaults to: nil)
  • custom_path_override (Pathname, nil) (defaults to: nil)
  • python_path_override (Pathname, nil) (defaults to: nil)


136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
# File 'lib/udb/resolver.rb', line 136

def initialize(
  repo_root = Udb.repo_root,
  schemas_path_override: nil,
  cfgs_path_override: nil,
  gen_path_override: nil,
  std_path_override: nil,
  custom_path_override: nil,
  python_path_override: nil
)
  @repo_root = repo_root
  @schemas_path = schemas_path_override || (@repo_root / "spec" / "schemas")
  @cfgs_path = cfgs_path_override || (@repo_root / "cfgs")
  @gen_path = gen_path_override || (@repo_root / "gen")
  @std_path = std_path_override || (@repo_root / "spec" / "std" / "isa")
  @custom_path = custom_path_override || (@repo_root / "spec" / "custom" / "isa")
  @python_path = python_path_override || (@repo_root / ".home" / ".venv" / "bin" / "python3")

  # cache of config names
  @cfg_info = T.let({}, T::Hash[T.any(String, Pathname), ConfigInfo])

  FileUtils.mkdir_p @gen_path
end

Instance Attribute Details

#cfgs_pathPathname (readonly)

path to find configuration files

Returns:

  • (Pathname)


87
88
89
# File 'lib/udb/resolver.rb', line 87

def cfgs_path
  @cfgs_path
end

#custom_pathPathname (readonly)

path to custom overlay specifications

Returns:

  • (Pathname)


99
100
101
# File 'lib/udb/resolver.rb', line 99

def custom_path
  @custom_path
end

#gen_pathPathname (readonly)

path to put generated files into

Returns:

  • (Pathname)


91
92
93
# File 'lib/udb/resolver.rb', line 91

def gen_path
  @gen_path
end

#python_pathPathname (readonly)

path to a python binary

Returns:

  • (Pathname)


115
116
117
# File 'lib/udb/resolver.rb', line 115

def python_path
  @python_path
end

#schemas_pathPathname (readonly)

path to find database schema files

Returns:

  • (Pathname)


83
84
85
# File 'lib/udb/resolver.rb', line 83

def schemas_path
  @schemas_path
end

#std_pathPathname (readonly)

path to the standard specification

Returns:

  • (Pathname)


95
96
97
# File 'lib/udb/resolver.rb', line 95

def std_path
  @std_path
end

Instance Method Details

#any_newer?(target, deps) ⇒ Boolean

returns true if either target does not exist, or if any of deps are newer than target

Parameters:

  • target (Pathname)
  • deps (Array<Pathname>)

Returns:

  • (Boolean)


161
162
163
164
165
166
167
# File 'lib/udb/resolver.rb', line 161

def any_newer?(target, deps)
  if target.exist?
    deps.any? { |d| target.mtime < d.mtime }
  else
    true
  end
end

#cfg_arch_for(config_path_or_name) ⇒ Udb::ConfiguredArchitecture

resolve the specification for a config, and return a ConfiguredArchitecture

Parameters:

  • config_path_or_name (Pathname, String)

Returns:



296
297
298
299
300
301
302
303
304
305
306
307
308
309
# File 'lib/udb/resolver.rb', line 296

def cfg_arch_for(config_path_or_name)
  config_info = cfg_info(config_path_or_name)

  @cfg_archs ||= {}
  return @cfg_archs[config_info.path] if @cfg_archs.key?(config_info.path)

  resolve_config(config_info.path)
  resolve_arch(config_info.unresolved_yaml)

  @cfg_archs[config_info.path] = Udb::ConfiguredArchitecture.new(
    config_info.name,
    Udb::AbstractConfig.create(gen_path / "cfgs" / "#{config_info.name}.yaml", config_info)
  )
end

#cfg_info(config_path_or_name) ⇒ ConfigInfo

Parameters:

  • config_path_or_name (Pathname, String)

Returns:



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
280
281
282
283
284
285
286
287
288
289
290
291
292
# File 'lib/udb/resolver.rb', line 252

def cfg_info(config_path_or_name)
  return @cfg_info.fetch(config_path_or_name) if config_path_or_name.is_a?(String) && @cfg_info.key?(config_path_or_name)
  return @cfg_info.fetch(config_path_or_name.realpath) if config_path_or_name.is_a?(Pathname) && @cfg_info.key?(config_path_or_name.realpath)

  config_path =
    case config_path_or_name
    when Pathname
      raise "Path does not exist: #{config_path_or_name}" unless config_path_or_name.file?

      config_path_or_name.realpath
    when String
      (@repo_root / "cfgs" / "#{config_path_or_name}.yaml").realpath
    else
      T.absurd(config_path_or_name)
    end

  config_yaml = YAML.safe_load_file(config_path)

  overlay_path =
    if config_yaml["arch_overlay"].nil?
      nil
    elsif Pathname.new(config_yaml["arch_overlay"]).exist?
      Pathname.new(config_yaml["arch_overlay"])
    elsif (@custom_path / config_yaml["arch_overlay"]).exist?
      @custom_path / config_yaml["arch_overlay"]
    else
      raise "Cannot resolve path to overlay (#{config_yaml["arch_overlay"]})"
    end

  info = ConfigInfo.new(
    name: config_yaml["name"],
    path: config_path,
    overlay_path:,
    unresolved_yaml: config_yaml,
    spec_path: std_path,
    merged_spec_path: @gen_path / "spec" / config_yaml["name"],
    resolved_spec_path: @gen_path / "resolved_spec" / config_yaml["name"]
  )
  @cfg_info[config_path] = info
  @cfg_info[info.name] = info
end

#merge_arch(config_yaml)

This method returns an undefined value.

Parameters:

  • config_yaml (Hash{String => T.untyped})


202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
# File 'lib/udb/resolver.rb', line 202

def merge_arch(config_yaml)
  config_name = config_yaml["name"]

  deps = Dir[std_path / "**" / "*.yaml"].map { |p| Pathname.new(p) }
  deps += Dir[custom_path / config_yaml["arch_overlay"] / "**" / "*.yaml"].map { |p| Pathname.new(p) } unless config_yaml["arch_overlay"].nil?

  overlay_path =
    if config_yaml["arch_overlay"].nil?
      nil
    else
      if config_yaml.fetch("arch_overlay")[0] == "/"
        Pathname.new(config_yaml.fetch("arch_overlay"))
      else
        custom_path / config_yaml.fetch("arch_overlay")
      end
    end
  raise "custom directory '#{overlay_path}' does not exist" if !overlay_path.nil? && !overlay_path.directory?

  if any_newer?(merged_spec_path(config_name) / ".stamp", deps)
    run [
      python_path.to_s,
      "#{Udb.gem_path}/python/yaml_resolver.py",
      "merge",
      std_path.to_s,
      overlay_path.nil? ? "/does/not/exist" : overlay_path.to_s,
      merged_spec_path(config_name).to_s
    ]
    FileUtils.touch(merged_spec_path(config_name) / ".stamp")
  end
end

#merged_spec_path(cfg_path_or_name) ⇒ Pathname

path to merged spec (merged with custom overley, but prior to resolution)

Parameters:

  • cfg_path_or_name (String, Pathname)

Returns:

  • (Pathname)


103
104
105
# File 'lib/udb/resolver.rb', line 103

def merged_spec_path(cfg_path_or_name)
  @gen_path / "spec" / cfg_info(cfg_path_or_name).name
end

#resolve_arch(config_yaml)

This method returns an undefined value.

Parameters:

  • config_yaml (Hash{String => T.untyped})


234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
# File 'lib/udb/resolver.rb', line 234

def resolve_arch(config_yaml)
  merge_arch(config_yaml)
  config_name = config_yaml["name"]

  deps = Dir[merged_spec_path(config_name) / "**" / "*.yaml"].map { |p| Pathname.new(p) }
  if any_newer?(resolved_spec_path(config_name) / ".stamp", deps)
    run [
      python_path.to_s,
      "#{Udb.gem_path}/python/yaml_resolver.py",
      "resolve",
      merged_spec_path(config_name).to_s,
      resolved_spec_path(config_name).to_s
    ]
    FileUtils.touch(resolved_spec_path(config_name) / ".stamp")
  end
end

#resolve_config(config_path) ⇒ Hash{String => T.untyped}

resolve config file and write it to gen_path returns the config data

Parameters:

  • config_path (Pathname)

Returns:

  • (Hash{String => T.untyped})


180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
# File 'lib/udb/resolver.rb', line 180

def resolve_config(config_path)
  config_info = cfg_info(config_path)
  return T.must(config_info.resolved_yaml) unless config_info.resolved_yaml.nil?

  resolved_config_yaml = T.let({}, T.nilable(T::Hash[String, T.untyped]))
  # write the config with arch_overlay expanded
  if any_newer?(gen_path / "cfgs" / "#{config_info.name}.yaml", [config_path])
    # is there anything to do here? validate?

    resolved_config_yaml = config_info.unresolved_yaml.dup
    resolved_config_yaml["$source"] = config_path.realpath.to_s

    FileUtils.mkdir_p gen_path / "cfgs"
    File.write(gen_path / "cfgs" / "#{config_info.name}.yaml", YAML.dump(resolved_config_yaml))
  else
    resolved_config_yaml = YAML.load_file(gen_path / "cfgs" / "#{config_info.name}.yaml")
  end

  config_info.resolved_yaml = resolved_config_yaml
end

#resolved_spec_path(cfg_path_or_name) ⇒ Pathname

path to merged and resolved spec

Parameters:

  • cfg_path_or_name (String, Pathname)

Returns:

  • (Pathname)


109
110
111
# File 'lib/udb/resolver.rb', line 109

def resolved_spec_path(cfg_path_or_name)
  @gen_path / "resolved_spec" / cfg_info(cfg_path_or_name).name
end

#run(cmd)

This method returns an undefined value.

run command in the shell. raise if exit is not zero

Parameters:

  • cmd (Array<String>)


171
172
173
174
175
# File 'lib/udb/resolver.rb', line 171

def run(cmd)
  puts cmd.join(" ")
  T.unsafe(self).send(:system, *cmd)
  raise unless $?.success?
end