class Rmk::LD

Linker tool for creating binaries and shared #libraries

Attributes

generated_executables[R]

query generated executables

ld[RW]

linker binary

lib_in_lib[RW]

force linking project library to #libraries

libraries[R]

#libraries (base names without prefix or suffix)

library_paths[R]

library paths

projects[R]

#projects that will be linked

transitive[RW]

enable #transitive linking for used #projects (#add_project)

use_rpath[RW]

use +-Wl,rpath=+ option for shared binaries

Public Class Methods

new() click to toggle source
Calls superclass method Rmk::LinkerLikeProcessor.new
# File tools/ld.rb, line 34
def initialize
  super

  data = tool_params(:LD, generator.configuration)
  @flags.add(data[:flags])
  @ld = data[:ld]

  @use_rpath = data[:use_rpath] || true
  @lib_in_lib = data[:lib_in_lib] || false
  @transitive = data[:transitive] || false

  @library_paths = []
  @libraries = []
  @projects = []
end

Public Instance Methods

add_library(*lib) click to toggle source

add a library (or list of #libraries), base name w/o prefix or suffix

# File tools/ld.rb, line 56
def add_library(*lib)
  @libraries += lib
end
add_path(*path) click to toggle source

add a library path (or list of paths)

# File tools/ld.rb, line 51
def add_path(*path)
  @library_paths += path
end
add_project(proj, options = {}) click to toggle source

add a project that should be used/linked

Calls superclass method Rmk::Processor#add_project
# File tools/ld.rb, line 67
def add_project(proj, options = {})
  super(proj, options)
  return if options[:no_library]

  cfg = options[:config] || generator.configuration

  path = generator.proj_build_directory(proj, cfg)
  add_path(path)

  lib = generator.proj_library_base_name(proj)
  add_library(lib)

  po = @projects.find { |p, _o| p == proj }

  if !po
    @projects << [proj, options]
  else
    # We don't want to have the same project in different configurations!
    pocfg = po[1][:config] || generator.configuration
    if pocfg != cfg
      @@log.warn "ignore new configuration '#{pocfg}' " \
                 "for project '#{proj}' (have '#{cfg}')"
    end
  end
end
ld=(cmd) click to toggle source
# File tools/ld.rb, line 28
def ld=(cmd)
  @vendor=nil
  @ld=cmd
end
libraries_text(with_projlib = true) click to toggle source
# File tools/ld.rb, line 150
def libraries_text(with_projlib = true)
  libs =
    if lib_object_files.empty? || !with_projlib
      @libraries
    else
      [generator.lib_base_name] + @libraries
    end

  (['-Wl,--start-group'] +
    libs.map { |name| "-l#{name}" } +
    ['-Wl,--end-group']).join(' ')
end
library_paths_text(with_projlib = true) click to toggle source
# File tools/ld.rb, line 128
def library_paths_text(with_projlib = true)
  paths = if with_projlib
            ['$builddir'] + @library_paths
          else
            @library_paths
          end

  paths.map do |p|
    q = if p.to_s =~ /\$builddir/
          next if lib_object_files.empty?
          p
        else
          Pathname.new(p).expand_path
        end
    if use_rpath
      "-L#{q} -Wl,-rpath,#{q}"
    else
      "-L#{q}"
    end
  end.join(' ')
end
ninja_begin(writer = generator.ninja) click to toggle source
Calls superclass method Rmk::LinkerLikeProcessor#ninja_begin
# File tools/ld.rb, line 180
def ninja_begin(writer = generator.ninja)
  super
  transitive_add! if transitive
  unique!
end
ninja_build(writer = generator.ninja) click to toggle source
# File tools/ld.rb, line 210
def ninja_build(writer = generator.ninja)
  sublibs = generator.subproject_targets # conservative: build all

  # add all used project libraries as dependencies
  #
  # TODO: Do we want this only for sub-projects? (Which would be a
  # "heuristic" and does not generally solve the 'no rule' problem
  # for external dependencies (rationale: order matters only
  # within subprojects)
  #
  projects.each do |p, opts|
    dir = generator.proj_build_directory(p, opts[:config])
    file = generator.proj_shared_object_file_name(p)
    sublibs << "#{dir}/#{file}"
  end
  sublibs = stable_unique(sublibs)

  unless lib_object_files.empty?
    lib = "$builddir/#{generator.this_shared_object_file_name}"
    writer.build(lib, 'ld_shared', lib_object_files,
                 order_only: sublibs)

    generator.add_to_intermediate_target(:libraries, lib)
    generator.add_to_toplevel_target(%i[all default], :libraries)
  end

  exe_targets = []

  exe_object_files.each do |f|
    base = strip_extension(f)
    exe = generator.exe(base).to_s
    writer.build(exe, 'ld_bin', [f],
                 implicit: lib, order_only: sublibs)
    exe_targets << exe
  end

  unless exe_targets.empty?
    generator.add_to_intermediate_target(:binaries, exe_targets)
  end

  @generated_executables = exe_targets

  shx_targets = []
  shx_object_files.each do |f|
    base = strip_extension(f[:file])
    shx = "#{f[:prefix]}#{base}#{f[:suffix]}"
    writer.build(shx, 'ld_shx', [f[:file]],
                 implicit: lib, order_only: sublibs)
    shx_targets << shx
  end

  unless shx_targets.empty?
    generator.add_to_intermediate_target(:binaries, shx_targets)
  end

  return if exe_targets.empty? && shx_targets.empty?

  generator.add_to_toplevel_target(%i[all default], :binaries)
end
ninja_end(writer = generator.ninja) click to toggle source
Calls superclass method Rmk::Processor#ninja_end
# File tools/ld.rb, line 270
def ninja_end(writer = generator.ninja)
  super
end
ninja_rules(writer = generator.ninja) click to toggle source
# File tools/ld.rb, line 198
def ninja_rules(writer = generator.ninja)
  writer.rule('ld_shared', command_link_shared_object,
              description: 'LD $out')
  writer.rule('ld_bin', command_link_executable,
              description: 'LD $out')

  return if shx_object_files.empty?

  writer.rule('ld_shx', command_link_shared_main_object,
              description: 'LD $out')
end
ninja_variables(writer = generator.ninja) click to toggle source
# File tools/ld.rb, line 186
def ninja_variables(writer = generator.ninja)
  writer.comment 'LD'
  writer.variable('ld', @ld)
  writer.variable('ldflags', flags.text)
  writer.variable('libs', [library_paths_text, libraries_text])
  if lib_in_lib
    writer.variable('extlibs',
                    [library_paths_text(false), libraries_text(false)])
  end
  writer.newline
end
remove_library(pattern) click to toggle source

remove #libraries (by pattern)

# File tools/ld.rb, line 61
def remove_library(pattern)
  pattern = Regexp.new(pattern) unless pattern.is_a?(Regexp)
  @libraries = @libraries.select { |lib| lib !~ pattern }
end
transitive_add!() click to toggle source

Called by generator: apply #transitive linking. Simple “transitive linking”: add #libraries and library paths from other #projects (one hop is enough as they should store their #libraries). Currently, this works only on system library level and not on packages level (generator.use_package).

# File tools/ld.rb, line 105
def transitive_add!
  @@log.debug 'transitive add libraries'

  @projects.each do |p, opts|
    @@log.debug "- from '#{p}' with options=#{opts.inspect}"
    cfg = opts[:config] || generator.configuration

    begin
      plibs = generator.proj_libraries(p, cfg)
    rescue StandardError => e
      @@log.error e
      next
    end

    next unless transitive && (opts[:transitive].nil? || opts[:transitive])
    @@log.debug "  - libraries: #{plibs[:libraries].inspect}"
    @@log.debug '  - paths: ' \
                "#{plibs[:library_paths].map(&:to_s).inspect}"
    add_library(*plibs[:libraries])
    add_path(*plibs[:library_paths])
  end
end
unique!() click to toggle source
# File tools/ld.rb, line 93
def unique!
  @libraries = stable_unique(@libraries)
  @library_paths = stable_unique_paths(@library_paths)
  @projects = stable_unique(@projects)
end