GithubHelp home page GithubHelp logo

synerbi / sirf-superbuild Goto Github PK

View Code? Open in Web Editor NEW
15.0 15.0 17.0 1.91 MB

SIRF CMake SuperBuild

Home Page: http://www.ccpsynerbi.ac.uk

License: Apache License 2.0

CMake 69.58% Shell 23.60% Dockerfile 1.71% HCL 3.50% Python 1.60%
image-reconstruction medical-imaging pet-mr sirf

sirf-superbuild's People

Contributors

alexjazz008008 avatar anderbiguri avatar ashgillman avatar bathomas avatar casperdcl avatar ckolbptb avatar danajk avatar evgueni-ovtchinnikov avatar foobar2016 avatar kristhielemans avatar lauramurgatroyd avatar paskino avatar vais-ral avatar

Stargazers

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

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

sirf-superbuild's Issues

changing version_config.cmake doesn't change git tags once already compiled

version_config.cmake sets SIRF_TAG as cached variables. However, this means that once you build, they never get changed (unless you force it). In particular, doing a git pull might change version_config.cmake but that still won't change the cahced variables, so you'll be building the same version.

The only way around it is to run cmake again, e.g.

cmake -DGadgetron_TAG=00b96376568278a595e78879026bb3b0d5fbb98d  -DGadgetron_URL=https://github.com/gadgetron/gadgetron .

This is in particular a problem for the VM, as updating the VM now no longer works as expected.

@casperdcl, any ideas?

Windows support

At present, most configurations will fail for Windows.
Probably our best strategy is to download precompiled packages and use those.

reduce number of things we build

I think we should save ourselves some time by disabling some options (like we did for STIR)

HDF5:

  • HDF5_BUILD_EXAMPLES=OFF
  • HDF5_BUILD_HL_LIB=OFF (to be tested)
  • HDF5_BUILD_TOOLS=OFF (or are these useful?)

Gadgetron:

  • Do we need Python gadgets? If not, we don’t need boost_python.
  • Do we need Gadgetron’s MATLAB support?

travis improvements

  • Put the Travis link on the SIRF github site
  • Put the Travis link on the SuperBuild github site
  • Sort-out the Travis ‘matrix’ when enabling both Linux and osx
  • Trigger the Travis build when SIRF (or other CCPPETMR forks) changes.
    (see SIRF#69, SIRF#71)

Travis ignores the global variables

I have a "global" section in .travis.yml but the BUILD_FLAGS there are ignored for some reason. At the moment I just duplicate them in the "env" section, but that will get ugly once we have more builds.

adding PYTHON_EXECUTABLE to env_ccppetmr.*?

With Python there's considerable scope for running the wrong version, i.e. not the one with which SIRF was compiled. We could reduce the chances for this happening by add a variable to our environment variables and tell people to use that one (or at least check what it is). could be called SIRF_PYTHON_EXECUTABLE or so.

Same for Matlab of course if multiple versions are installed.

what do people think?

Building with rpaths on OSX High Sierra

With OSX, dynamic libraries can be referred to as (using the example of libboostfilesystem.dylib):

  1. libboostfilesystem.dylib,
  2. @rpath/libboostfilesystem.dylib, or
  3. /absolute/path/to/libboostfilesystem.dylib.

Compilation against the first of these three causes linking errors on my version of OSX. This is possibly due to changes to SIP for High Sierra (or possible a problem specific to my machine).

Possible solutions include:

  1. using install_name_tool to change the first option to the second or third, or
  2. linking against static libraries.

Hopefully this issue will sort itself out over time.

setup environment variables at the end of the SuperBuild

Make the SuperBuild create a “env_ccppetmr.sh” (and .csh?), and later even a Windows version, all installed in the ${INSTALL}/bin directory. Then we tell people to run the SuperBuild, and source the appropriate .sh/.csh, either from the command line or from their .bashrc (or whatever the appropriate file is for the shell they are using).

This should be easy enough using
https://cmake.org/cmake/help/v3.2/command/configure_file.html

we make a skeleton (per shell-type) (based on our current .sirfc) like this

SIRF_PATH=@SIRF_SRC_PATH@
LD_LIBRARY_PATH=@CCPPETMR_INSTALL@/lib:$LD_LIBRARY_PATH
PYTHONPATH=@CCPPETMR_INSTALL@/python:$PYTHONPATH
PATH=$PATH:@CCPPETMR_INSTALL@/bin
export SIRF_PATH
export LD_LIBRARY_PATH
export PYTHONPATH

and make 2 CMake variables accordingly. Then we just add a configure_file in SuperBuild.cmake statement and that’s it (I think).

previous install can cause compilation problems for newer versions

external packages are installed (by default in INSTALL, and therefore include files in INSTALL/include). This works fine at first build. However, when the include files of one of the external packages get updated, it is possible that the installed versions get found first (as opposed to the include files of the external package), resulting in compilation failures or segmentation faults.

Example:

Boost gets installed in INSTALL/include. STIR as well. STIR's CMakeLists.txt has

include_directories(${Boost_INCLUDE_DIRS})

before adding its own include directory to the compiler path. This is arguably a bug in STIR, but it is possible that other packages have the same problem.

Possible solution

If every package would be installed in its own directory, this problem would not occur. This is somewhat ugly though and means we will need to take care of passing the relevant paths along.

how to pass options to projects

Examples:

  • on the UCL CS CentOS system, we need to set -DMKLROOT_PATH= for Gadgetron's CMake as its MKL is too old (it will then use ACE).

  • I'd like to be able to enable STIR_OPENMP for testing.

I guess, this can be done by first running "cmake .;make", then going into the relevant directory, running cmake-gui there and changing the configuration, but this seems rather painful.

Using different compilers does not always go to dependencies

In bash, you can normally say

CC=gcc-5 CXX=g++-5 cmake .

to use a non-default compiler. However, these settings of CC do not get passed on to (some?) dependencies with the superbuild. It currently only works if you do

export CC=gcc-5
export CXX=g++-5
cmake .

Handle different options for different versions of dependencies

With the version tracking, we can use the SuperBuild to use different versions of SIRF, STIR etc. However, some CMake options will be version specific. Example, SyneRBI/SIRF#41 will need Matlab_ROOT_DIR as opposed to MATLAB_ROOT. The SuperBuild needs to know about this, but how would it?

In principle, we could keep a list of commits where the option changed, and then check if it's an ancester of the desired TAG using something like

git rev-list ${COMMIT_HASH_WITH_NEW_OPTION}| grep $(git rev-parse ${${SIRF_GIT_TAG})

(after CMake-ifying this) but keeping the list is error-prone and tedious.

Running Travis on SIRF branch or PRs

We’ve merged Edo’s version-tracking branch on the SuperBuild. There’s now a way to use specify BUILD_DEVEL=On to use the master branch of STIR/ISMRMRD/Gadgetron/SIRF. This is tested in Travis as well now.

Now we need a way to test a specific branch on SIRF before we merge, ideally we can also do that with a PR. Any ideas anyone?

See also #17

Linux link failure when using python 3.5

current link errors are like

[ 27%] Linking CXX executable gadgetron_test_python

/usr/lib/gcc/x86_64-linux-gnu/5/../../../x86_64-linux-gnu/libboost_python.so: undefined reference to `PyString_InternFromString'

This is with USE_SYSTEM_BOOST=On but using a virtualenv for Python 3.5 (Python 2.7 works). It seems Gadgetron needs to link with libboost_python-py35.so.

what to do with BUILD_TESTING?

Several sub-projects have BUILD_TESTING.
We could provide several options (no testing, test only SIRF, test everything). In some sense however the "test only SIRF" is flawed as the dependencies need to work correctly. But running all the tests is going to take a looong time.

I guess we could have a CMake option for every project dependency (could maybe be done semi-automatically: add an option BUILD_TESTING_${proj}, which then sets its BUILD_TESTING).

This would allow the developer to choose what tests to run.

Set environment variables on build

After build the user must manually source the env_ccppetmr.sh file to set the environment variables.

Also Gadgetron doesn't have a default configuration, so it will not start.

SIRF-SuperBuild Ubuntu 17.04 installation failed

Hi all,
I tried to install the SIRF-SuperBuild on my Linux (Ubuntu 17.04) box following the instractions from the web page: https://github.com/CCPPETMR/SIRF/wiki/SIRF-SuperBuild-Ubuntu-16.04
I had to install some packages before but when I run the steps:
cmake .
make
in section 3 I get en error message when the configuration of gadgetron is performed without any obvious reason.
This is what I get:

[ 90%] Completed 'Armadillo'
[ 90%] Built target Armadillo
Scanning dependencies of target Gadgetron
[ 92%] Creating directories for 'Gadgetron'
[ 93%] Performing download step (git clone) for 'Gadgetron'
Cloning into 'Gadgetron'...
Already on 'master'
Your branch is up-to-date with 'origin/master'.
[ 94%] No patch step for 'Gadgetron'
[ 95%] Performing update step for 'Gadgetron'
Current branch master is up to date.
[ 96%] Performing configure step for 'Gadgetron'
-- The C compiler identification is GNU 6.3.0
-- The CXX compiler identification is GNU 6.3.0
-- Check for working C compiler: /usr/bin/cc
-- Check for working C compiler: /usr/bin/cc -- works
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Detecting C compile features
-- Detecting C compile features - done
-- Check for working CXX compiler: /usr/bin/c++
-- Check for working CXX compiler: /usr/bin/c++ -- works
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Looking for packages in : /home/evan/Documents/devel/SIRF-SuperBuild/INSTALL/
-- Found Git: /usr/bin/git (found version "2.11.0")
64bit system is found
-- Try OpenMP C flag = [-fopenmp]
-- Performing Test OpenMP_FLAG_DETECTED
-- Performing Test OpenMP_FLAG_DETECTED - Success
-- Try OpenMP CXX flag = [-fopenmp]
-- Performing Test OpenMP_FLAG_DETECTED
-- Performing Test OpenMP_FLAG_DETECTED - Success
-- Found OpenMP: -fopenmp
OpenMP multithreading enabled
-- Looking for pthread.h
-- Looking for pthread.h - found
-- Looking for pthread_create
-- Looking for pthread_create - found
-- Found Threads: TRUE
-- Boost version: 1.63.0
-- Found the following Boost libraries:
-- system
-- thread
-- program_options
-- filesystem
-- timer
-- chrono
-- date_time
-- atomic
-- Found ACE library: /usr/lib/libACE.so
-- Found ACE include: /usr/include
-- Found CUDA: /usr (found suitable version "8.0", minimum required is "5.5")
Compiling with -gencode arch=compute_30,code=sm_30;-gencode arch=compute_35,code=sm_35;-gencode arch=compute_50,code=sm_50;-gencode arch=compute_52,code=sm_52
-- Found GTest: /home/evan/Documents/devel/SIRF-SuperBuild/INSTALL/lib/libgtest.a
-- Found Armadillo: /home/evan/Documents/devel/SIRF-SuperBuild/INSTALL/lib/libarmadillo.so (found suitable version "7.800.2", minimum required is "4.600")
Armadillo found. Note that ARMADILLO_BLAS_LONG_LONG must be defined in /home/evan/Documents/devel/SIRF-SuperBuild/INSTALL/include/armadillo_bits/config.hpp to link against the MKL ILP64 interface.
-- Found HDF5: /home/evan/Documents/devel/SIRF-SuperBuild/INSTALL/lib/libhdf5_cpp-shared.so;/home/evan/Documents/devel/SIRF-SuperBuild/INSTALL/lib/libhdf5-shared.so (found suitable version "1.10.0.1", minimum required is "1.8") found components: C CXX HL
FFTW3 UNIX libraries: /home/evan/Documents/devel/SIRF-SuperBuild/INSTALL/lib/libfftw3f.a/home/evan/Documents/devel/SIRF-SuperBuild/INSTALL/lib/libfftw3.a
-- Found FFTW3: /home/evan/Documents/devel/SIRF-SuperBuild/INSTALL/lib/libfftw3f.a
MKL is NOT found ...
-- Looking for sgemm_
-- Looking for sgemm_ - found
-- A library with BLAS API found.
-- A library with BLAS API found.
-- Looking for cheev_
-- Looking for cheev_ - found
-- A library with LAPACK API found.
LAPACK Found
-- Found PythonLibs: /usr/lib/x86_64-linux-gnu/libpython2.7.so (found suitable version "2.7.13", minimum required is "2")
-- Found PythonInterp: /usr/bin/python2.7 (found version "2.7.13")
-- Found NumPy: version "1.12.1" /usr/lib/python2.7/dist-packages/numpy/core/include
-- Could NOT find Matlab (missing: MATLAB_ROOT MATLAB_INCLUDE_DIR MATLAB_LIBRARIES)
Searching for OpenGL, GLEW, GLUT, and Qt. These libraries are only used in a single standalone application and are thus non-essential.
-- Found OpenGL: /usr/lib/x86_64-linux-gnu/libGL.so
GLEW NOT FOUND
-- Could NOT find GLUT (missing: GLUT_glut_LIBRARY GLUT_INCLUDE_DIR)
qmake: could not exec '/usr/lib/x86_64-linux-gnu/qt4/bin/qmake': No such file or directory
-- Found unsuitable Qt version "" from NOTFOUND
PLplot is found at /usr/include/plplot
-- Found HDF5: /home/evan/Documents/devel/SIRF-SuperBuild/INSTALL/lib/libhdf5_cpp-shared.so;/home/evan/Documents/devel/SIRF-SuperBuild/INSTALL/lib/libhdf5-shared.so (found version "1.10.0.1") found components: HL
Compiling cpu based optical flow registration toolbox.
Compiling gpu based optical flow registration toolbox.
-- Boost version: 1.63.0
-- Found the following Boost libraries:
-- date_time
-- filesystem
-- system
-- thread
-- regex
-- chrono
-- atomic
-- Found ACE library: /usr/lib/libACE.so
-- Found ACE include: /usr/include
-- Boost version: 1.63.0
-- A library with BLAS API found.
-- A library with BLAS API found.
-- A library with LAPACK API found.
-- Boost version: 1.63.0
-- Found the following Boost libraries:
-- python
-- system
-- thread
-- chrono
-- date_time
-- atomic
-- Found PythonLibs: /usr/lib/x86_64-linux-gnu/libpython2.7.so (found version "2.7.13")
-- Boost version: 1.63.0
-- Found the following Boost libraries:
-- python
-- Boost version: 1.63.0
-- Found the following Boost libraries:
-- thread
-- system
-- chrono
-- date_time
-- atomic
-- Found HDF5: /home/evan/Documents/devel/SIRF-SuperBuild/INSTALL/lib/libhdf5_cpp-shared.so;/home/evan/Documents/devel/SIRF-SuperBuild/INSTALL/lib/libhdf5-shared.so (found suitable version "1.10.0.1", minimum required is "1.8") found components: C
-- Boost version: 1.63.0
-- Found the following Boost libraries:
-- program_options
-- thread
-- system
-- chrono
-- date_time
-- atomic
Matlab not found. Matlab wrapper for registration toolbox will not be compiled.
MATLAB NOT FOUND: matlab wrapper for gtplus toolbox will not be compiled.
-- Found HDF5: /home/evan/Documents/devel/SIRF-SuperBuild/INSTALL/lib/libhdf5_cpp-shared.so;/home/evan/Documents/devel/SIRF-SuperBuild/INSTALL/lib/libhdf5-shared.so (found version "1.10.0.1") found components: HL
-- Found ACE library: /usr/lib/libACE.so
-- Found ACE include: /usr/include
FFTW3 UNIX libraries: /home/evan/Documents/devel/SIRF-SuperBuild/INSTALL/lib/libfftw3f.a/home/evan/Documents/devel/SIRF-SuperBuild/INSTALL/lib/libfftw3.a/home/evan/Documents/devel/SIRF-SuperBuild/INSTALL/lib/libfftw3f.a/home/evan/Documents/devel/SIRF-SuperBuild/INSTALL/lib/libfftw3.a
ZFP NOT Found
-- Found HDF5: /home/evan/Documents/devel/SIRF-SuperBuild/INSTALL/lib/libhdf5_cpp-shared.so;/home/evan/Documents/devel/SIRF-SuperBuild/INSTALL/lib/libhdf5-shared.so (found suitable version "1.10.0.1", minimum required is "1.8") found components: C
Cuda found, compiling gpu accelerated gadgets
-- Boost version: 1.63.0
-- Found the following Boost libraries:
-- python
-- system
-- thread
-- chrono
-- date_time
-- atomic
PYTHON_INCLUDE_DIRS: /usr/include/python2.7
PYTHON_LIBRARIES: /usr/lib/x86_64-linux-gnu/libpython2.7.so
NUMPY_INCLUDE_DIRS: /usr/lib/python2.7/dist-packages/numpy/core/include
Compiling Python Gadgets
-- /home/evan/Documents/devel/SIRF-SuperBuild/INSTALL/include/home/evan/Documents/devel/SIRF-SuperBuild/INSTALL/lib/libboost_python.so/home/evan/Documents/devel/SIRF-SuperBuild/INSTALL/lib/libboost_system.so/home/evan/Documents/devel/SIRF-SuperBuild/INSTALL/lib/libboost_thread.so/home/evan/Documents/devel/SIRF-SuperBuild/INSTALL/lib/libboost_chrono.so/home/evan/Documents/devel/SIRF-SuperBuild/INSTALL/lib/libboost_date_time.so/home/evan/Documents/devel/SIRF-SuperBuild/INSTALL/lib/libboost_atomic.so
-- Could NOT find DCMTK (missing: DCMTK_config_INCLUDE_DIR DCMTK_ofstd_INCLUDE_DIR DCMTK_ofstd_LIBRARY DCMTK_oflog_INCLUDE_DIR DCMTK_oflog_LIBRARY DCMTK_dcmdata_INCLUDE_DIR DCMTK_dcmdata_LIBRARY DCMTK_dcmimgle_INCLUDE_DIR DCMTK_dcmimgle_LIBRARY)
DCMTK NOT found, not compiling DICOM gadget
-- Found Doxygen: /usr/bin/doxygen (found version "1.8.13")
CMake Error at chroot/CMakeLists.txt:41 (get_filename_component):
get_filename_component unknown component
/usr/lib/x86_64-linux-gnu/libcublas_device.a

CPACK_PACKAGING_INSTALL_PREFIX: /home/evan/Documents/devel/SIRF-SuperBuild/INSTALL
-- Found CPack generators: DEB
-- Configuring incomplete, errors occurred!
See also "/home/evan/Documents/devel/SIRF-SuperBuild/Gadgetron-prefix/src/Gadgetron-build/CMakeFiles/CMakeOutput.log".
CMakeFiles/Gadgetron.dir/build.make:111: recipe for target 'Gadgetron-prefix/src/Gadgetron-stamp/Gadgetron-configure' failed
make[2]: *** [Gadgetron-prefix/src/Gadgetron-stamp/Gadgetron-configure] Error 1
CMakeFiles/Makefile2:338: recipe for target 'CMakeFiles/Gadgetron.dir/all' failed
make[1]: *** [CMakeFiles/Gadgetron.dir/all] Error 2
Makefile:83: recipe for target 'all' failed
make: *** [all] Error 2

Any suggestions ?
Regards
Evan

Travis builds with system SWIG, HDF5 and FFTW3

I noticed that in the .travis.yml we use
EXTRA_BUILD_FLAGS="-DUSE_SYSTEM_SWIG=ON -DUSE_SYSTEM_HDF5=ON -DUSE_SYSTEM_FFTW3=ON

Is that correct? Should not we use the SWIG, HDF5 and FFTW3 that are built as part of the SuperBuild? Or these settings do not do anything?

usage of `mark_as_superbuild`

I don't think we use this properly.

SuperBuild.cmake should add some variables to all subprojects

Indeed, it tries to do this (could do it for more of them I think). However, it says

mark_as_superbuild(
   PROJECTS ALL_PROJECTS
   VARS CMAKE_GENERATOR:STRING CMAKE_GENERATOR_PLATFORM:STRING CMAKE_GENERATOR_TOOLSET:STRING
        CMAKE_C_COMPILER:FILEPATH CMAKE_CXX_COMPILER:FILEPATH
        CMAKE_INSTALL_PREFIX:PATH
)

I think this has to be

mark_as_superbuild(
   ALL_PROJECTS
 ..
)

No idea why it would work now... but I think it does as I put it in :-)

All the external projects use it to pass CMake variables on

code is

mark_as_superbuild(
    VARS
        ${externalProjName}_DIR:PATH
    LABELS
      "FIND_PACKAGE"
  )

This has 2 problems as far as I can see:

  • It is only correct if the project indeeds sets/need the *_DIR variable (many do not, e.g. GTEST_ROOT).
  • According to the doc, mark_as_superbuild adds this variable then only to the top-level project, which in our case is CCPPETMR, but not to the projects where they are actually needed.

The 2nd item seems to imply these lines are currently effectively ignored. Hence the need for us to specify all the variables by hand again.

I wonder if we should use ALL_PROJECTS here as well, although then we're going to add variables where we actually don't need/use them. A better way would be to know to which projects we need to add them, but then we introduce dependencies on "downstream" projects, which seems a bad idea.

Probably a better solution is to forget about mark_as_superbuild in the sub-projects, and to introduce a variable with all CMake args that need to be set if someone wants to use this project (but that seems exactly what mark_as_superbuild seems to aim to provide).

Summary

I'm confused...

Here's an old-ish post with some info. It refers to old Slicer stuff, but here's a recent link. Note that this also has some interesting stuff with unset and ExternalProject_SetIfNotDefined which might be relevant to #51.

problems with finding Python

CMake's FindPythonInterp doesn't necessarily find the Python that you expect. this currently breaks Travis for Python 3.5.

see comments in #32 for potential resolution

changing an External*.cmake doesn't cause rebuild

I ran "cmake .; make", then updated External_Boost_configureboost.cmake (by switching branch), which added some extra boost libraries, but those libraries never got build. This might be a CMake problem, not ours.

CMake version 3.7.0.

add dependency version control

See discussion started on the mailing list

Currently the SuperBuild uses master at our CCPPETMR forks of dependencies such as STIR and Gadgetron. This means that it is impossible to go back to a specific version with all dependencies that same as when we released.

One possibility is to create a SIRF/sirf_version_config.cmake (recording some git-tags) as that'd would be version tracked. A difficulty with this is that the Superbuild clones/builds dependencies first, and hence doesn't have SIRF and so its version info yet. I think it's therefore better to keep this version_config.cmake in the Superbuild. SIRF's CMake files would just have to set minimum requirements as usual (and would conform to how our external dependencies Gadgetron are treated).

However, hard-wiring specific SIRF git-hashes makes it inconvenient for developers. On the other hand, setting it to master means we cannot rely on this to get a specific version after all. (we could have a release branch that modifies the version_config.cmake, but that will create complications/conflicts etc). Maybe we could let these be overridden by CMake variables?

Not sure what the best option is.

deleting CMake Cache in the superbuild doesn't wipe the slate

I'm used to deleting the cache (via the CMake GUI, or by hand by deleting CMakeCache.txt) to restart, but that doesn't work as expected: the "external" projects were still configured as before. So, resetting options might not actually work, which probably explains what I saw in #7 when rebuilding. This sounds a bit like a CMake bug to me, but not sure how to really test this. suggestions welcome.

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.