Coder Social home page Coder Social logo

matsoftware / swift-code-metrics Goto Github PK

View Code? Open in Web Editor NEW
325.0 5.0 13.0 733 KB

Code metric analyzer for Swift projects.

License: MIT License

Python 90.52% Swift 8.81% Shell 0.67%
swift architecture ios macos metrics analysis dependency-graph swift-dependencies-visualizer dependency-tree dependency

swift-code-metrics's Introduction

FOSSA Status License Build Status codecov Codacy Badge PyPI

swift-code-metrics

Code metrics analyzer for Swift projects.

Example code distribution Example deviation main sequence
Example internal distribution

Introduction

The goal of this software is to provide an insight of the architectural state of a software written in Swift that consists in several modules. Inspired by the book of Robert C. Martin, Clean Architecture, the software will scan the project to identify the different components in order to assess several common code metrics in the software industry:

  • the overall number of concrete classes and interfaces
  • the instability and abstractness of the framework
  • the distance from the main sequence
  • LOC (Lines Of Code)
  • NOC (Numbers Of Comments)
  • POC (Percentage Of Comments)
  • NOM (Number of Methods)
  • Number of concretes (Number of classes and structs)
  • NOT (Number Of Tests)
  • NOI (Number Of Imports)
  • Frameworks dependency graph (number of internal and external dependencies)

Requirements

This is a Python 3 script that depends on matplotlib, adjustText, pyfunctional and pygraphviz.

This latest package depends on the Graphviz binary that must be installed before. If you're in a Mac environment, you can install it directly with brew install graphviz.

Usage

The package is available on pip with pip3 install swift-code-metrics.

The syntax is:

swift-code-metrics --source <path-to-swift-project> --artifacts <output-directory> --exclude <excluded-folders> --tests-paths <test-paths> --generate-graphs

  • --source is the path to the folder that contains the main Xcode project or Workspace
  • --artifacts path to the folder that will contain the generated output.json report
  • --exclude (optional) space separated list of path substrings to exclude from analysis (e.g. Tests will ignore all files/folders that contain Tests)
  • --tests-paths (default: Test Tests) space separated list of path substrings matching test classes
  • --generate-graphs (optional) if passed, it will generate the graphs related to the analysis and save them in the artifacts folder

Development

Please run ./install.sh and ./build_and_test.sh to install dependencies and run the tests.

The repo comes with a predefined setup for VS Code to debug and run tests as well.

Documentation

Please follow the guide with a practical example to get started.

Current limitations

  • This tool is designed for medium/large codebases composed by different frameworks. The script will scan the directory and it will identify the frameworks by the name of the 'root' folder, so it's strictly dependent on the file hierarchy (unless a project path override file is specified)

  • Libraries built with spm are not supported.

  • The framework name is inferred using the directory structure. If the file is in the root dir, the default_framework_name will be used. No inspection of the xcodeproj will be made.

  • The list of methods currently doesn't support computed vars

  • Inline comments in code (such as struct Data: {} //dummy data) are currently not supported

  • Only XCTest test frameworks are currently supported

TODOs

  • Code improvements
  • Other (open to suggestions)

Contact

Mattia Campolese

swift-code-metrics's People

Contributors

codacy-badger avatar dale-stewart avatar dependabot[bot] avatar krugerk avatar matsoftware avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

swift-code-metrics's Issues

Specify version on numpy

When running on a new installation with the latest numpy (1.20.2) i got a crash

Traceback (most recent call last):
  File "/usr/local/bin/swift-code-metrics", line 8, in <module>
    sys.exit(main())
  File "/usr/local/lib/python3.9/site-packages/swift_code_metrics/scm.py", line 76, in main
    from ._graphs_renderer import GraphsRender
  File "/usr/local/lib/python3.9/site-packages/swift_code_metrics/_graphs_renderer.py", line 5, in <module>
    from ._graphs_presenter import GraphPresenter
  File "/usr/local/lib/python3.9/site-packages/swift_code_metrics/_graphs_presenter.py", line 2, in <module>
    from ._graph_helpers import Graph
  File "/usr/local/lib/python3.9/site-packages/swift_code_metrics/_graph_helpers.py", line 3, in <module>
    import matplotlib.pyplot as plt
  File "/usr/local/lib/python3.9/site-packages/matplotlib/__init__.py", line 107, in <module>
    from . import _api, cbook, docstring, rcsetup
  File "/usr/local/lib/python3.9/site-packages/matplotlib/rcsetup.py", line 26, in <module>
    from matplotlib.colors import Colormap, is_color_like
  File "/usr/local/lib/python3.9/site-packages/matplotlib/colors.py", line 82, in <module>
    from matplotlib import _api, cbook, scale
  File "/usr/local/lib/python3.9/site-packages/matplotlib/scale.py", line 18, in <module>
    from matplotlib.ticker import (
  File "/usr/local/lib/python3.9/site-packages/matplotlib/ticker.py", line 179, in <module>
    from matplotlib import transforms as mtransforms
  File "/usr/local/lib/python3.9/site-packages/matplotlib/transforms.py", line 43, in <module>
    from numpy.linalg import inv
ImportError: cannot import name 'inv' from 'numpy.linalg' (unknown location)

It was fixed by installing numpy 1.19.5

iMac:wine-scanner-ios kasper$ pip3 uninstall numpy
Found existing installation: numpy 1.20.2
Uninstalling numpy-1.20.2:
  Would remove:
    /usr/local/lib/python3.9/site-packages/numpy-1.20.2.dist-info/*
    /usr/local/lib/python3.9/site-packages/numpy/.dylibs/libgcc_s.1.dylib
    /usr/local/lib/python3.9/site-packages/numpy/.dylibs/libgfortran.3.dylib
    /usr/local/lib/python3.9/site-packages/numpy/.dylibs/libopenblas.0.dylib
    /usr/local/lib/python3.9/site-packages/numpy/.dylibs/libquadmath.0.dylib
Proceed (y/n)? y
  Successfully uninstalled numpy-1.20.2
iMac:wine-scanner-ios kasper$ pip3 install numpy==1.19.5
Collecting numpy==1.19.5
  Downloading numpy-1.19.5-cp39-cp39-macosx_10_9_x86_64.whl (15.6 MB)
     |████████████████████████████████| 15.6 MB 5.8 MB/s 
Installing collected packages: numpy
Successfully installed numpy-1.19.5

pip3 python3.7 error

Exception:
Traceback (most recent call last):
File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/pip/_vendor/urllib3/response.py", line 331, in _error_catcher
yield
File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/pip/_vendor/urllib3/response.py", line 413, in read
data = self._fp.read(amt)
File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/pip/_vendor/cachecontrol/filewrapper.py", line 62, in read
data = self.__fp.read(amt)
File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/http/client.py", line 447, in read
n = self.readinto(b)
File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/http/client.py", line 491, in readinto
n = self.fp.readinto(b)
File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/socket.py", line 589, in readinto
return self._sock.recv_into(b)
File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/ssl.py", line 1049, in recv_into
return self.read(nbytes, buffer)
File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/ssl.py", line 908, in read
return self._sslobj.read(len, buffer)
socket.timeout: The read operation timed out

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/pip/_internal/cli/base_command.py", line 143, in main
status = self.run(options, args)
File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/pip/_internal/commands/install.py", line 318, in run
resolver.resolve(requirement_set)
File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/pip/_internal/resolve.py", line 102, in resolve
self._resolve_one(requirement_set, req)
File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/pip/_internal/resolve.py", line 256, in _resolve_one
abstract_dist = self._get_abstract_dist_for(req_to_install)
File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/pip/_internal/resolve.py", line 209, in _get_abstract_dist_for
self.require_hashes
File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/pip/_internal/operations/prepare.py", line 283, in prepare_linked_requirement
progress_bar=self.progress_bar
File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/pip/_internal/download.py", line 836, in unpack_url
progress_bar=progress_bar
File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/pip/_internal/download.py", line 673, in unpack_http_url
progress_bar)
File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/pip/_internal/download.py", line 897, in _download_http_url
_download_url(resp, link, content_file, hashes, progress_bar)
File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/pip/_internal/download.py", line 617, in _download_url
hashes.check_against_chunks(downloaded_chunks)
File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/pip/_internal/utils/hashes.py", line 48, in check_against_chunks
for chunk in chunks:
File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/pip/_internal/download.py", line 585, in written_chunks
for chunk in chunks:
File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/pip/_internal/utils/ui.py", line 159, in iter
for x in it:
File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/pip/_internal/download.py", line 574, in resp_read
decode_content=False):
File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/pip/_vendor/urllib3/response.py", line 465, in stream
data = self.read(amt=amt, decode_content=decode_content)
File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/pip/_vendor/urllib3/response.py", line 430, in read
raise IncompleteRead(self._fp_bytes_read, self.length_remaining)
File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/contextlib.py", line 130, in exit
self.gen.throw(type, value, traceback)
File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/pip/_vendor/urllib3/response.py", line 336, in _error_catcher
raise ReadTimeoutError(self._pool, None, 'Read timed out.')
pip._vendor.urllib3.exceptions.ReadTimeoutError: HTTPSConnectionPool(host='files.pythonhosted.org', port=443): Read timed out.

Crash trying to run with generate-graphs

I'm trying to run the command below but always crash.

Swift Code Metrics info

swift-code-metrics --version
swift-code-metrics 1.5.1

graphviz info

 brew info graphviz 
graphviz: stable 2.47.0 (bottled), HEAD
Graph visualization software from AT&T and Bell Labs
https://www.graphviz.org/
/usr/local/Cellar/graphviz/2.47.0 (301 files, 10.5MB) *
  Poured from bottle on 2021-03-27 at 12:24:11
From: https://github.com/Homebrew/homebrew-core/blob/HEAD/Formula/graphviz.rb
License: EPL-1.0
==> Dependencies
Build: autoconf ✔, automake ✔, bison ✘, pkg-config ✔
Required: gd ✔, gts ✔, libpng ✔, librsvg ✔, libtool ✔, pango ✔
==> Options
--HEAD
	Install HEAD version
==> Analytics
install: 79,005 (30 days), 201,535 (90 days), 631,622 (365 days)
install-on-request: 66,252 (30 days), 166,056 (90 days), 507,447 (365 days)
build-error: 0 (30 days)

Run log

swift-code-metrics --source . --artifacts metrics --exclude Pods --generate-graphs
NotificationServiceExtension is not linked with the rest of the project.
NotificationServiceExtension is not linked with the rest of the project.
Traceback (most recent call last):
  File "/usr/local/bin/swift-code-metrics", line 8, in <module>
    sys.exit(main())
  File "/usr/local/lib/python3.9/site-packages/swift_code_metrics/scm.py", line 76, in main
    from ._graphs_renderer import GraphsRender
  File "/usr/local/lib/python3.9/site-packages/swift_code_metrics/_graphs_renderer.py", line 5, in <module>
    from ._graphs_presenter import GraphPresenter
  File "/usr/local/lib/python3.9/site-packages/swift_code_metrics/_graphs_presenter.py", line 2, in <module>
    from ._graph_helpers import Graph
  File "/usr/local/lib/python3.9/site-packages/swift_code_metrics/_graph_helpers.py", line 8, in <module>
    import pygraphviz as pgv
  File "/usr/local/lib/python3.9/site-packages/pygraphviz/__init__.py", line 58, in <module>
    from .agraph import AGraph, Node, Edge, Attribute, ItemAttribute, DotError
  File "/usr/local/lib/python3.9/site-packages/pygraphviz/agraph.py", line 22, in <module>
    from . import graphviz as gv
  File "/usr/local/lib/python3.9/site-packages/pygraphviz/graphviz.py", line 28, in <module>
    _graphviz = swig_import_helper()
  File "/usr/local/lib/python3.9/site-packages/pygraphviz/graphviz.py", line 24, in swig_import_helper
    _mod = imp.load_module('_graphviz', fp, pathname, description)
  File "/usr/local/Cellar/[email protected]/3.9.2_3/Frameworks/Python.framework/Versions/3.9/lib/python3.9/imp.py", line 242, in load_module
    return load_dynamic(name, filename, file)
  File "/usr/local/Cellar/[email protected]/3.9.2_3/Frameworks/Python.framework/Versions/3.9/lib/python3.9/imp.py", line 342, in load_dynamic
    return _load(spec)
ImportError: dlopen(/usr/local/lib/python3.9/site-packages/pygraphviz/_graphviz.cpython-39-darwin.so, 2): Symbol not found: _PyIOBase_Type
  Referenced from: /usr/local/lib/python3.9/site-packages/pygraphviz/_graphviz.cpython-39-darwin.so
  Expected in: flat namespace
 in /usr/local/lib/python3.9/site-packages/pygraphviz/_graphviz.cpython-39-darwin.so

RecursionError: maximum recursion depth exceeded while calling a Python object

~/Deskto: swift-code-metrics --source SwiftNetFrame --artifacts SwiftNetFrameTest --generate-graphs
Traceback (most recent call last):
File "/usr/local/bin/swift-code-metrics", line 10, in
sys.exit(main())
File "/usr/local/lib/python3.7/site-packages/swift_code_metrics/scm.py", line 69, in main
if not analyzer.analyze():
File "/usr/local/lib/python3.7/site-packages/swift_code_metrics/_analyzer.py", line 21, in analyze
self.__analyze_directory(self.directory, self.exclude_paths, self.tests_default_suffixes)
File "/usr/local/lib/python3.7/site-packages/swift_code_metrics/_analyzer.py", line 116, in __analyze_directory
swift_file = SwiftFileParser(file=full_path, base_path=directory, is_test=is_in_test_path).parse()
File "/usr/local/lib/python3.7/site-packages/swift_code_metrics/_parser.py", line 99, in parse
framework_name=self.__framework_name(),
File "/usr/local/lib/python3.7/site-packages/swift_code_metrics/_parser.py", line 113, in __framework_name
return self.__extracted_framework_name() + suffix
File "/usr/local/lib/python3.7/site-packages/swift_code_metrics/_parser.py", line 117, in __extracted_framework_name
first_subpath = self.__extract_first_subpath(subdir)
File "/usr/local/lib/python3.7/site-packages/swift_code_metrics/_parser.py", line 126, in __extract_first_subpath
return self.__extract_first_subpath(subdirs[0])
File "/usr/local/lib/python3.7/site-packages/swift_code_metrics/_parser.py", line 126, in __extract_first_subpath
return self.__extract_first_subpath(subdirs[0])
File "/usr/local/lib/python3.7/site-packages/swift_code_metrics/_parser.py", line 126, in __extract_first_subpath
return self.__extract_first_subpath(subdirs[0])
[Previous line repeated 987 more times]
File "/usr/local/lib/python3.7/site-packages/swift_code_metrics/_parser.py", line 124, in __extract_first_subpath
subdirs = os.path.split(subdir)
File "/usr/local/Cellar/python/3.7.2_2/Frameworks/Python.framework/Versions/3.7/lib/python3.7/posixpath.py", line 108, in split
sep = _get_sep(p)
File "/usr/local/Cellar/python/3.7.2_2/Frameworks/Python.framework/Versions/3.7/lib/python3.7/posixpath.py", line 42, in _get_sep
if isinstance(path, bytes):
RecursionError: maximum recursion depth exceeded while calling a Python object

Frameworks under the same project

The library cannot detect when one or more frameworks are included in the same project and therefore they're included in the same folder.

A solution is to provide a json manifest file to put in the root of the folder to explicitly tell SCM which libraries are included.

Overlapping values

Hi, I'm currently using the swift-code-metrics 1.4.1 and as you can see the code distribution graph have overlapping values.

Screen Shot 2020-08-02 at 09 44 40

Thanks

@testable imports not included

The way imports are currently counted prevents @testable imports from being included in the count. For modules with large numbers of tests this can significantly affect the statistics produced.

Eg:

  • import MyPackage is counted as an import.
  • @testable import MyPackage is not.

ZeroDivisionError for new xcode project

For example project it works fine, and I receive full report, but the problem starts when I try to use it with my own projects.

I am creating new empty xcode project, with name „TestRaport”, then I run in terminal : swift-code-metrics --source /Desktop/TestRaport --artifacts report --generate-graphs

And I am getting python error:
Traceback (most recent call last): File "/Library/Frameworks/Python.framework/Versions/3.7/bin/swift-code-metrics", line 11, in <module> sys.exit(main()) File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/swift_code_metrics/scm.py", line 67, in main analyzer = Inspector(directory, artifacts, default_tests_paths, exclude) File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/swift_code_metrics/_analyzer.py", line 18, in init self._save_report(artifacts) File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/swift_code_metrics/_analyzer.py", line 43, in _save_report json.dump(self.report.as_dict, fp, indent=4) File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/swift_code_metrics/_analyzer.py", line 218, in as_dict self.non_test_frameworks_key: self.non_test_framework_aggregate.as_dict, File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/swift_code_metrics/_analyzer.py", line 187, in as_dict "poc": ReportingHelpers.decimal_format(self.poc) File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/swift_code_metrics/_analyzer.py", line 175, in poc return Metrics.percentage_of_comments(self.noc, self.loc) File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/swift_code_metrics/_metrics.py", line 117, in percentage_of_comments return 100 * noc / (noc + loc) ZeroDivisionError: division by zero

Declare project metadata

The output.json file should contain a section with:

  • project name (that can be defined in the scm.json)
  • git references

Error running 1.5.0

I upgraded to the lastest version and now I can't run

Traceback (most recent call last):
  File "/usr/local/bin/swift-code-metrics", line 5, in <module>
    from swift_code_metrics.scm import main
  File "/Library/Python/2.7/site-packages/swift_code_metrics/scm.py", line 5, in <module>
    from ._helpers import Log
  File "/Library/Python/2.7/site-packages/swift_code_metrics/_helpers.py", line 12
    def warn(cls, message: str):
                         ^
SyntaxError: invalid syntax

Installing with Mac OS X

How to install on Mac...

python3 -m venv .venv
source .venv/bin/activate
brew install graphviz
pip install swift-code-metrics

I would have added this to Wiki -- but it was not turned on ... couldn't access.

How to: Install on Apple Silicon

I have struggled a bit with installing on the M1 Apple Silicon, but finally managed to get it working.

Here are the steps.

Install a 3.x version of Python with pyenv.
Skip this step if you already have Python 3.x installed or want to install it with another manager

These steps are based on https://laict.medium.com/install-python-on-macos-11-m1-apple-silicon-using-pyenv-12e0729427a9

Install pyenv
brew instal pyenv

Install Python 3.8.6
pyenv install --patch 3.8.6 <<(curl -sSL https://raw.githubusercontent.com/Homebrew/formula-patches/113aa84/python/3.8.3.patch\?full_index\=1)

Set the Python as system default
pyenv global 3.8.6

Install Graphviz and Pygraphviz

Install Graphviz
brew install graphviz

Install Pygraphviz (from pygraphviz/pygraphviz#155 (comment))
pip install --global-option=build_ext --global-option="-I$(brew --prefix graphviz)/include" --global-option="-L$(brew --prefix graphviz)/lib" pygraphviz

Install swift-code-metrics

Install the master branch of swift-code-metrics
pip install --global-option=build_ext --global-option="-I$(brew --prefix graphviz)/include" --global-option="-L$(brew --prefix graphviz)/lib" "git+https://github.com/matsoftware/swift-code-metrics.git#egg=swift-code-metrics"

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.