Skip to content

Issue#14: Ignore unknown header includes #15

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 4 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
76 changes: 7 additions & 69 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,75 +5,13 @@
"version": "0.2.0",
"configurations": [
{
"name": "Launch index.html",
"type": "chrome",
"type": "rdbg",
"name": "Rspec",
"request": "launch",
"breakOnLoad": true,
"sourceMapPathOverrides": {
"webRoot": "${workspaceRoot}"
},
"file": "${workspaceRoot}/index.html"
},
{
"name": "Debug Local File",
"type": "Ruby",
"request": "launch",
"cwd": "${workspaceRoot}",
"program": "${workspaceRoot}/exe/cpp_dependency_graph",
"useBundler": true,
"pathToBundler": "C:/tools/ruby25/bin/bundle.bat",
"pathToRDebugIDE": "C:/tools/ruby25/bin/rdebug-ide.bat",
"showDebuggerOutput": true,
"args": ["visualise_components", "-r", "../TileDB"]
},
{
"name": "Listen for rdebug-ide",
"type": "Ruby",
"request": "attach",
"cwd": "${workspaceRoot}",
"remoteHost": "127.0.0.1",
"remotePort": "1234",
"remoteWorkspaceRoot": "${workspaceRoot}"
},
{
"name": "Rails server",
"type": "Ruby",
"request": "launch",
"cwd": "${workspaceRoot}",
"program": "${workspaceRoot}/bin/rails",
"args": [
"server"
]
},
{
"name": "RSpec - all",
"type": "Ruby",
"request": "launch",
"cwd": "${workspaceRoot}",
"program": "C:/tools/ruby25/bin/rspec",
"args": [
"-I",
"${workspaceRoot}"
]
},
{
"name": "RSpec - active spec file only",
"type": "Ruby",
"request": "launch",
"cwd": "${workspaceRoot}",
"program": "C:/tools/ruby25/bin/rspec",
"args": [
"-I",
"${workspaceRoot}",
"${file}"
]
},
{
"name": "Cucumber",
"type": "Ruby",
"request": "launch",
"cwd": "${workspaceRoot}",
"program": "${workspaceRoot}/bin/cucumber"
"command": "bundle exec rspec",
"script": "${file}:${lineNumber}",
"args": [],
"askParameters": false
}
]
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ def all_cyclic_links

def links(component_name)
component = @project.source_component(component_name)
p component
source_files = component.source_files
external_includes = @project.external_includes(component)
source_files.map do |file|
Expand Down
37 changes: 37 additions & 0 deletions lib/cpp_dependency_graph/include_dependency.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# frozen_string_literal: true

# Represents a #include in a source file
class IncludeDependency
def initialize(raw_include)
@raw_include = raw_include
p = Pathname.new(@raw_include)
@dir_name = p.dirname
@basename = p.basename.to_s
@absolute_include = @dir_name == Pathname.new('.')
end

def relative?
!@absolute_include
end

def absolute?
@absolute_include
end

def component
return @dir_name.to_s unless absolute?

''
end

def ==(other)
raw_include == other.raw_include
end
alias eql? ==

def hash
[self.class, @raw_include].hash
end

attr_reader :basename, :raw_include
end
7 changes: 5 additions & 2 deletions lib/cpp_dependency_graph/include_to_component_resolver.rb
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,12 @@ def external_includes(component)
end

def component_for_include(include)
return '' unless source_files.key?(include)
return '' unless source_files.key?(include.basename)

@component_include_map_cache[include] = component_for_include_private(include) unless @component_include_map_cache.key?(include)
component = component_for_include_private(include.basename) unless @component_include_map_cache.key?(include.basename)
return '' if !include.component.empty? and include.component != component

@component_include_map_cache[include] = component
@component_include_map_cache[include]
end

Expand Down
5 changes: 4 additions & 1 deletion lib/cpp_dependency_graph/project.rb
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,10 @@ def source_files

def dependencies(component)
# TODO: This is repeating the same work twice! component_for_include is called when calling external_includes
external_includes(component).map { |include| @include_resolver.component_for_include(include) }.reject(&:empty?).uniq
external_includes_component = external_includes(component)
external_includes_component.map do |include|
@include_resolver.component_for_include(include)
end.reject(&:empty?).uniq
end

def external_includes(component)
Expand Down
9 changes: 8 additions & 1 deletion lib/cpp_dependency_graph/source_component.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
# frozen_string_literal: true

require 'pathname'

require_relative 'config'
require_relative 'include_dependency'
require_relative 'directory_parser'
require_relative 'source_file'

Expand All @@ -24,13 +27,17 @@ def source_files
end

def includes
@includes ||= source_files.flat_map(&:includes).uniq.map { |include| File.basename(include) }
@includes ||= source_files.flat_map(&:includes).uniq.map { |f| IncludeDependency.new(f) }.uniq
end

def loc
@loc ||= source_files.inject(0) { |total_loc, file| total_loc + file.loc }
end

def exists?
File.exist?(@path)
end

private

def parse_source_files(extensions)
Expand Down
4 changes: 4 additions & 0 deletions lib/cpp_dependency_graph/source_file.rb
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,10 @@ def loc
@loc ||= file_contents.lines.count
end

def exists?
File.exist?(path)
end

private

def all_includes
Expand Down
1 change: 0 additions & 1 deletion spec/test/example_project/Engine/Engine.cpp
Original file line number Diff line number Diff line change
@@ -1,3 +1,2 @@
#include "DataAccess/DA.h"
#include "Engine.h"

38 changes: 38 additions & 0 deletions spec/test/include_dependency_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# frozen_string_literal: true

require 'cpp_dependency_graph/include_dependency'

RSpec.describe IncludeDependency do
it 'relative? returns false for an absolute include' do
i = IncludeDependency.new('Engine.h')
expect(i.relative?).to be false
expect(i.absolute?).to be true
end

it 'relative? returns true for a relative include' do
i = IncludeDependency.new('Engine/Engine.h')
expect(i.relative?).to be true
expect(i.absolute?).to be false
end

it 'component returns empty for a relative include' do
i = IncludeDependency.new('Engine.h')
expect(i.component).to eq('')
end

it 'component returns parent directory for a relative include' do
i = IncludeDependency.new('Engine/Engine.h')
expect(i.component).to eq('Engine')
end

it 'returns the correct basename' do
i = IncludeDependency.new('Engine/Engine.h')
expect(i.basename).to eq('Engine.h')
end

it 'compares correctly against other instances' do
i1 = IncludeDependency.new('Engine/Engine.h')
i2 = IncludeDependency.new('Engine/Engine.h')
expect(i1).to eq(i2)
end
end
14 changes: 7 additions & 7 deletions spec/test/project_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -25,17 +25,17 @@
expect(component.source_files.size).to eq(0)
end

it 'returns dependencies of component' do
project = Project.new('spec/test/example_project')
component = project.source_component('Engine')
dependencies = project.dependencies(component)
expect(dependencies).to include('Framework', 'UI', 'DataAccess')
end

it 'all source files of project' do
project = Project.new('spec/test/example_project')
source_files = project.source_files.values.map(&:basename)
expect(source_files).to contain_exactly('DA.h', 'Display.cpp', 'Display.h', 'Engine.cpp', 'Engine.h', 'Engine.h',
'OldEngine.h', 'System.cpp', 'System.h', 'framework.h', 'main.cpp')
end

it 'returns dependencies of component' do
project = Project.new('spec/test/example_project')
component = project.source_component('Engine')
dependencies = project.dependencies(component)
expect(dependencies).to include('Framework', 'UI', 'DataAccess')
end
end
16 changes: 14 additions & 2 deletions spec/test/source_component_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,16 @@
require 'cpp_dependency_graph/source_component'

RSpec.describe SourceComponent do
it 'exists? returns true for a valid component' do
component = SourceComponent.new('spec/test/example_project/Engine')
expect(component.exists?).to be true
end

it 'exists? returns false for a valid component' do
component = SourceComponent.new('spec/test/example_project/Engine2')
expect(component.exists?).to be false
end

it 'has a name attribute that matches the directory' do
component = SourceComponent.new('spec/test/example_project/Engine')
expect(component.name).to eq('Engine')
Expand All @@ -18,9 +28,11 @@
expect(source_file_names).to contain_exactly('Engine.cpp', 'Engine.h', 'OldEngine.h')
end

it 'has an includes attribute' do
it 'outputs the correct includes for component' do
component = SourceComponent.new('spec/test/example_project/Engine')
expect(component.includes).to contain_exactly('framework.h', 'Display.h', 'DA.h', 'Engine.h')
expect(component.includes).to contain_exactly(an_object_having_attributes(basename: 'framework.h'),
an_object_having_attributes(basename: 'Display.h'), an_object_having_attributes(basename: 'DA.h'),
an_object_having_attributes(basename: 'Engine.h'))
end

it 'has a loc (lines of code) attribute' do
Expand Down
10 changes: 10 additions & 0 deletions spec/test/source_file_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,14 @@
source_file = SourceFile.new('spec/test/example_project/Engine/Engine.cpp')
expect(source_file.loc).to be > 0
end

it 'exists? returns true for a file that exists' do
source_file = SourceFile.new('spec/test/example_project/Engine/Engine.cpp')
expect(source_file.exists?).to be true
end

it 'exists? returns false for a file that does not exist' do
source_file = SourceFile.new('spec/test/example_project/Engine/Engine2.cpp')
expect(source_file.exists?).to be false
end
end