Class: Includes

Inherits:
Object show all
Defined in:
lib/ceedling/includes/includes.rb

Overview

=========================================================================

Ceedling - Test-Centered Build System for C ThrowTheSwitch.org Copyright (c) 2010-26 Mike Karlesky, Mark VanderVoord, & Greg Williams SPDX-License-Identifier: MIT

Class Method Summary collapse

Class Method Details

.contains?(includes, filename) ⇒ Boolean

Class method to check for a filename in the collection

Returns:

  • (Boolean)


93
94
95
# File 'lib/ceedling/includes/includes.rb', line 93

def self.contains?(includes, filename)
  includes.any? { |include| include.filename == filename }
end

.filter(includes, pattern) ⇒ Object

Class method to extract all matching includes by filename pattern



78
79
80
# File 'lib/ceedling/includes/includes.rb', line 78

def self.filter(includes, pattern)
  includes.select { |include| include.filename =~ pattern }
end

.from_hashes(hashes) ⇒ Array<Include>

Class method to convert a list of hashes back into Include objects

Examples:

hashes = [
  { 'type' => 'user', 'filepath' => 'header.h' },
  { 'type' => 'system', 'filepath' => 'stdio.h' },
  { 'type' => 'user', 'filepath' => 'module.h' }
]
Include.from_hashes(hashes)
# => [
#   UserInclude.new("header.h"),
#   SystemInclude.new("stdio.h"),
#   UserInclude.new("module.h")
# ]

Parameters:

  • hashes (Array<Hash>)

    Array of hashes with 'type' and 'filepath' keys

Returns:

  • (Array<Include>)

    List of UserInclude and SystemInclude objects

Raises:

  • (ArgumentError)

    If hash is missing required keys or has invalid type



59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
# File 'lib/ceedling/includes/includes.rb', line 59

def self.from_hashes(hashes)
  return hashes.map do |hash|
    raise ArgumentError, "Hash missing 'type' key" unless hash.key?('type')
    raise ArgumentError, "Hash missing 'filepath' key" unless hash.key?('filepath')
    
    case hash['type']
    when 'user'
      UserInclude.new(hash['filepath'])
    when 'mock'
      MockInclude.new(hash['filepath'])
    when 'system'
      SystemInclude.new(hash['filepath'])
    else
      raise ArgumentError, "Invalid include type: #{hash['type']}. Must be 'user' or 'system'"
    end
  end
end

.reconcile(bare:, user:, system:) ⇒ Object

Class method to reconcile bare, user, and system includes returning a list of reconciled user and system includes.

Purpose

Bare include preprocessing extracts user and system includes, but there's no way to explicitly differentiate these. Meanwhile, by necessity, user and system include extraction can identify too many includes. This class method uses the knowledeg of the different types of extraction to reconcile the two lists. It accomplishes:

  1. Paring down system includes to the include directives used in original file.
  2. Paring down user includes to the include directives used in original file.
  3. Reconciling a list of user & system includes properly distinguished.

Method

Compares bare includes against user and system includes and applies the following rules:

  1. Intersection of bare includes and system includes.
  2. Intersection of bare includes and user includes.


156
157
158
159
160
161
162
163
164
165
166
167
168
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
196
197
# File 'lib/ceedling/includes/includes.rb', line 156

def self.reconcile(bare:, user:, system:)
  # Validate input types

  # `bare` can only be base Include objects, no sub-classes.
  unless bare.is_a?(Array) && bare.all? { |include| include.class == Include }
    raise ArgumentError, "`bare` must be an Array of Include objects"
  end

  # Ensure `user` is an array of UserInclude objects or sub-classes
  unless user.is_a?(Array) && user.all? { |include| include.is_a?(UserInclude) }    
    raise ArgumentError, "`user` must be an Array of UserInclude objects"
  end
  
  # Ensure `system` is an array of SystemInclude objects or sub-classes
  unless system.is_a?(Array) && system.all? { |include| include.is_a?(SystemInclude) }    
    raise ArgumentError, "`system` must be an Array of SystemInclude objects"
  end

  return [] if bare.empty?

  system_includes = []
  user_includes = []

  # Create set of bare include filenames for O(1) lookup
  bare_filenames = Set.new(bare.map(&:filename))

  # Intersect system includes with bare includes based on filename.
  # Keep system includes that have matching filenames in bare list.
  system_includes = system.select do |include|
    bare_filenames.include?(include.filename)
  end

  # Intersect user includes with bare includes based on filename.
  # Keep user includes (including subclasses) that have matching filenames in bare list.
  user_includes = user.select do |include|
    bare_filenames.include?(include.filename)
  end    

  # Construct reconciled list of includes with reconciled results.
  # Always system includes first (C best practice).
  return (system_includes + user_includes)
end

.sanitize(includes, &block) {|include| ... } ⇒ Array<Include>

Class method for non-mutating sanitize

Examples:

Basic usage

Includes.sanitize(includes)

Custom rejection

Includes.sanitize(includes) { |include, all| ... }

Parameters:

  • includes (Array<Include>)

    List of includes to sanitize

  • block (Proc)

    Optional block passed to reject! for custom filtering

Yields:

  • (include)

    Each include object for custom rejection logic

Returns:

  • (Array<Include>)

    New sanitized list



107
108
109
110
111
# File 'lib/ceedling/includes/includes.rb', line 107

def self.sanitize(includes, &block)
  _includes = includes.clone
  self.sanitize!(_includes, &block)
  return _includes
end

.sanitize!(includes, &block) {|include| ... } ⇒ Array<Include>

Class method for mutating sanitize

Examples:

Basic usage

Includes.sanitize!(includes)

Custom rejection

Includes.sanitize!(includes) { |include, all| ... }

Parameters:

  • includes (Array<Include>)

    List of includes to sanitize in place

  • block (Proc)

    Optional block passed to reject! for custom filtering

Yields:

  • (include)

    Each include object for custom rejection logic

Returns:

  • (Array<Include>)

    The modified includes list



123
124
125
126
127
128
129
130
131
132
133
134
135
136
# File 'lib/ceedling/includes/includes.rb', line 123

def self.sanitize!(includes, &block)
  # Remove any duplicates
  includes.uniq!

  # Apply custom rejection with access to full list if block provided
  if block_given?
    includes.reject! { |include| block.call(include, includes) }
  end

  # Ensure system includes come first
  self.sort!(includes)

  return includes
end

.sort(includes) ⇒ Object

Sort list so system includes are at the beginning (Best practice)



201
202
203
204
205
# File 'lib/ceedling/includes/includes.rb', line 201

def self.sort(includes)
  _includes = includes.clone
  self.sort!(_includes)
  return _includes
end

.sort!(includes) ⇒ Object



207
208
209
210
# File 'lib/ceedling/includes/includes.rb', line 207

def self.sort!(includes)
  includes.sort_by! { |include| include.is_a?(SystemInclude) ? 0 : 1 }
  return includes
end

.system(includes) ⇒ Object

Class method to extract all system includes



83
84
85
# File 'lib/ceedling/includes/includes.rb', line 83

def self.system(includes)
  includes.select { |include| include.is_a?(SystemInclude) }
end

.to_hashes(includes) ⇒ Array<Hash>

Class method to convert mixed list of Include objects into an order-preserving list of hashes

Examples:

includes = [
  UserInclude.new("header.h"),
  SystemInclude.new("stdio.h"),
  UserInclude.new("module.h")
]
Include.to_hash(includes)
# => [
#   { 'type' => 'user', 'filepath' => 'header.h' },
#   { 'type' => 'system', 'filepath' => 'stdio.h' },
#   { 'type' => 'user', 'filepath' => 'module.h' }
# ]

Parameters:

  • includes (Array<Include>)

    List of UserInclude and SystemInclude objects

Returns:

  • (Array<Hash>)

    Array of hashes, each with 'type' and 'filepath' keys



25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
# File 'lib/ceedling/includes/includes.rb', line 25

def self.to_hashes(includes)
  return includes.map do |include|
    type = 
      case include
      when MockInclude then 'mock'
      when UserInclude then 'user'
      when SystemInclude then 'system'
      else raise ArgumentError, "Unknown Include type: #{include.class}"
      end

    {
      'type' => type,
      'filepath' => include.filepath,
    }
  end
end

.user(includes) ⇒ Object

Class method to extract all user includes



88
89
90
# File 'lib/ceedling/includes/includes.rb', line 88

def self.user(includes)
  includes.select { |include| include.is_a?(UserInclude) }
end