From 20e3912a5f3b1cf51745a79497003b0476ac3c0a Mon Sep 17 00:00:00 2001 From: Andrew Pam Date: Wed, 27 Nov 2019 14:58:16 +1100 Subject: [PATCH] Add bindings_perl and examples/perl_api_example --- CMakeLists.txt | 14 ++++ README.md | 19 +++++ bindings_perl/CMakeLists.txt | 45 +++++++++++ examples/perl_api_example/CMakeLists.txt | 46 ++++++++++++ examples/perl_api_example/README.md | 30 ++++++++ examples/perl_api_example/data/file.pl | 7 ++ .../perl_api_example.srctrlprj.in | 20 +++++ examples/perl_api_example/src/example.pl | 75 +++++++++++++++++++ 8 files changed, 256 insertions(+) create mode 100644 bindings_perl/CMakeLists.txt create mode 100644 examples/perl_api_example/CMakeLists.txt create mode 100644 examples/perl_api_example/README.md create mode 100755 examples/perl_api_example/data/file.pl create mode 100644 examples/perl_api_example/perl_api_example.srctrlprj.in create mode 100755 examples/perl_api_example/src/example.pl diff --git a/CMakeLists.txt b/CMakeLists.txt index 5026d28..e45964c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,6 +2,7 @@ cmake_minimum_required (VERSION 2.6) # --- Options --- +set(BUILD_BINDINGS_PERL OFF CACHE BOOL "Build the SourcetrailDB Perl bindings.") set(BUILD_BINDINGS_PYTHON OFF CACHE BOOL "Build the SourcetrailDB Python bindings.") set(BUILD_BINDINGS_JAVA OFF CACHE BOOL "Build the SourcetrailDB Java bindings.") set(BUILD_EXAMPLES ON CACHE BOOL "Build the examples.") @@ -45,6 +46,19 @@ set(CORE_BINARY_DIR "${CMAKE_BINARY_DIR}/core") # can be accessed in subdirector add_subdirectory(${CORE_SOURCE_DIR} ${CORE_BINARY_DIR}) +# --- Perl Binding --- + +if (BUILD_BINDINGS_PERL) + message(STATUS "The SourcetrailDB Perl bindings will be built.") + + set(PERL_BINDING_TARGET_NAME "bindings_perl") + set(PERL_BINDING_OUTPUT_DIR "") + add_subdirectory("${CMAKE_SOURCE_DIR}/bindings_perl" "${CMAKE_BINARY_DIR}/bindings_perl") +else() + message(STATUS "Building the SourcetrailDB Perl bindings will be skipped. You can enable building this target by setting 'BUILD_BINDINGS_PERL' to 'ON'.") +endif() + + # --- Python Binding --- if (BUILD_BINDINGS_PYTHON) diff --git a/README.md b/README.md index f2b4025..0cd1e43 100644 --- a/README.md +++ b/README.md @@ -35,6 +35,7 @@ __To get an overview on everything involved, please take a look at our [Language Even though the core implementation is written in C++, this does not require you to write your indexer in C++ as well. Instead you can use a language binding (e.g. see [SWIG](http://www.swig.org/)). These language bindings are already available: +* Perl (via [SWIG](http://www.swig.org/)) * Python (via [SWIG](http://www.swig.org/)) * Java (via [SWIG](http://www.swig.org/)) @@ -94,6 +95,24 @@ $ make test_core $ ./core/test_core ``` +### Perl Bindings + +Requirements: +* [Perl](https://www.perl.org/) needs to be included and linked against when building the Perl bindings. CMake will auto-detect your Perl installation. If you want to build against a specific version of Perl, please define the `PERL_LIBRARY` variable accordingly when running CMake. Make sure to link to the correct Perl version when building for different architectures (32bit/64bit). + +* [SWIG 3.0.12](http://www.swig.org/) is used to automatically generate Perl binding code. Make sure that SWIG is added to your path environment variable. + +If you want to build the Perl bindings run: +``` +$ cd path/to/SourcetrailDB +$ mkdir build +$ cd build +$ cmake -DBUILD_BINDINGS_PERL=ON .. +$ make sourcetraildb +``` + +Swig is configured to generate the Perl binding code as a pre-build event, so you don't need to bother with updating manually. + ### Python Bindings Requirements: diff --git a/bindings_perl/CMakeLists.txt b/bindings_perl/CMakeLists.txt new file mode 100644 index 0000000..ad7d90a --- /dev/null +++ b/bindings_perl/CMakeLists.txt @@ -0,0 +1,45 @@ +cmake_minimum_required (VERSION 2.6) + +# --- Setup Paths --- + +set(RESOURCES_SWIG_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../resources_swig") +set(GENERATED_SRC_DIR "${CMAKE_CURRENT_BINARY_DIR}/src") +set(PERL_BINDING_OUTPUT_DIR ${CMAKE_CURRENT_BINARY_DIR} PARENT_SCOPE) + +set(SWIG_INTERFACE_FILE "${RESOURCES_SWIG_DIR}/interface/sourcetraildb.i") + + +# --- Find Perl --- + +find_package(PerlLibs ${PERL_VERSION} REQUIRED) + + +# --- Find Swig --- + +find_package(SWIG REQUIRED) +include(${SWIG_USE_FILE}) + + +# --- Configure Target --- + +set_source_files_properties(${SWIG_INTERFACE_FILE} PROPERTIES CPLUSPLUS ON) + +include_directories( + "${RESOURCES_SWIG_DIR}/include" + "${CORE_SOURCE_DIR}/include" + ${PERL_INCLUDE_DIRS} +) + +swig_add_library( + ${PERL_BINDING_TARGET_NAME} + TYPE SHARED + LANGUAGE perl + OUTPUT_DIR ${PERL_BINDING_OUTPUT_DIR} + OUTFILE_DIR ${GENERATED_SRC_DIR} + SOURCES ${SWIG_INTERFACE_FILE} ${RESOURCES_SWIG_DIR}/src/sourcetraildb.cpp +) + +set_target_properties(${PERL_BINDING_TARGET_NAME} + PROPERTIES OUTPUT_NAME sourcetraildb) + +swig_link_libraries(${PERL_BINDING_TARGET_NAME} ${PERL_LIBRARIES} ${LIB_CORE_TARGET_NAME}) diff --git a/examples/perl_api_example/CMakeLists.txt b/examples/perl_api_example/CMakeLists.txt new file mode 100644 index 0000000..15cacce --- /dev/null +++ b/examples/perl_api_example/CMakeLists.txt @@ -0,0 +1,46 @@ +cmake_minimum_required (VERSION 2.6) + +set(EXAMPLE_NAME "perl_api_example") + +# --- Configure Sourcetrail Project File --- + +add_custom_target( + ${EXAMPLE_NAME} ALL + COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/src/example.pl ${CMAKE_CURRENT_BINARY_DIR}/example.pl +) + +add_dependencies(${EXAMPLE_NAME} _${PERL_BINDING_TARGET_NAME}) + +if (MSVC) + STRING(REGEX REPLACE "/" "\\\\" BACKSLASHED_PERL_BINDING_OUTPUT_DIR ${PERL_BINDING_OUTPUT_DIR}) + STRING(REGEX REPLACE "/" "\\\\" BACKSLASHED_CMAKE_CURRENT_BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}) + + add_custom_command( + TARGET ${EXAMPLE_NAME} POST_BUILD + COMMAND xcopy \"${BACKSLASHED_PERL_BINDING_OUTPUT_DIR}\\$(Configuration)\\sourcetraildb*\" \"${BACKSLASHED_CMAKE_CURRENT_BINARY_DIR}\" /Y + ) + add_custom_command( + TARGET ${EXAMPLE_NAME} POST_BUILD + COMMAND xcopy \"${BACKSLASHED_PERL_BINDING_OUTPUT_DIR}\\sourcetraildb.pm\" \"${BACKSLASHED_CMAKE_CURRENT_BINARY_DIR}\" /Y + ) +else() + add_custom_command( + TARGET ${EXAMPLE_NAME} POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy ${PERL_BINDING_OUTPUT_DIR}/sourcetraildb* ${CMAKE_CURRENT_BINARY_DIR} + ) + + if (APPLE) + add_custom_command( + TARGET ${EXAMPLE_NAME} POST_BUILD + COMMAND ${CMAKE_COMMAND} -E rename ${CMAKE_CURRENT_BINARY_DIR}/sourcetraildb.dylib ${CMAKE_CURRENT_BINARY_DIR}/sourcetraildb.so + ) + endif() +endif() + + +set(PERL_API_EXAMPLE_DATA_PATH "${CMAKE_CURRENT_SOURCE_DIR}/data") + +configure_file( + "${CMAKE_CURRENT_SOURCE_DIR}/${EXAMPLE_NAME}.srctrlprj.in" + "${CMAKE_CURRENT_BINARY_DIR}/${EXAMPLE_NAME}.srctrlprj" +) diff --git a/examples/perl_api_example/README.md b/examples/perl_api_example/README.md new file mode 100644 index 0000000..54788aa --- /dev/null +++ b/examples/perl_api_example/README.md @@ -0,0 +1,30 @@ + +## Python API Example + +### Requirements + +* CMake +* C++ Compiler +* Perl +* SWIG + +### Running from Command Line + +* Run cmake with `BUILD_BINDINGS_PERL=ON` +* Build target `sourcetraildb` +* Copy two files from the `bindings_perl` build directory into `SourcetrailDB/examples/perl_api_example/src`: + - `sourcetraildb.pm` + - Windows: `sourcetraildb.dll` + - Linux: `sourcetraildb.so` + - macOS: `sourcetraildb.dylib` and rename to `sourcetraildb.so` +* Run example from the directory `SourcetrailDB/examples/perl_api_example/src` with: + +``` +$ perl example.pl --database-file-path=absolute/path/to/SourcetrailDB/examples/perl_api_example/src/example.srctrldb --source-file-path=absolute/path/to/SourcetrailDB/examples/perl_api_example/data/file.pl +``` + +### Running with Sourcetrail + +* Run cmake with `BUILD_EXAMPLES=ON` and `BUILD_BINDINGS_PERL=ON` +* Build target `sourcetraildb` and `perl_api_example`. +* Open `perl_api_exampl.srctrlprj` located in the build directory of `perl_api_example` with Sourcetrail and index the project. diff --git a/examples/perl_api_example/data/file.pl b/examples/perl_api_example/data/file.pl new file mode 100755 index 0000000..d31d71e --- /dev/null +++ b/examples/perl_api_example/data/file.pl @@ -0,0 +1,7 @@ +package MyClass; + +my $my_member = 1; + +sub my_method { + return $my_member; +} diff --git a/examples/perl_api_example/perl_api_example.srctrlprj.in b/examples/perl_api_example/perl_api_example.srctrlprj.in new file mode 100644 index 0000000..e26f947 --- /dev/null +++ b/examples/perl_api_example/perl_api_example.srctrlprj.in @@ -0,0 +1,20 @@ + + + + + perl example.pl --database-file-path=%{DATABASE_FILE_PATH} --source-file-path=%{SOURCE_FILE_PATH} --database-version=%{DATABASE_VERSION} + Custom Command Source Group + 0 + + .pl + .pm + + + @PERL_API_EXAMPLE_DATA_PATH@/file.pl + + enabled + Custom Command Source Group + + + 7 + diff --git a/examples/perl_api_example/src/example.pl b/examples/perl_api_example/src/example.pl new file mode 100755 index 0000000..5bc088e --- /dev/null +++ b/examples/perl_api_example/src/example.pl @@ -0,0 +1,75 @@ +#!/usr/bin/perl + +use v5.10; + +use strict; +use warnings; + +use Getopt::Long; + +use FindBin; +use lib $FindBin::Bin; +use sourcetraildb; + +my ( $database_file_path, $source_file_path ); +my $db_version = 0; + +GetOptions( + 'database-file-path=s' => \$database_file_path, + 'source-file-path=s' => \$source_file_path, + 'database-version=i' => \$db_version, +); + +say "SourcetrailDB Perl API Example"; +my $supported_db_version = sourcetraildb::getSupportedDatabaseVersion; +say "Supported database version: $supported_db_version"; + +die "ERROR: Only supports database version: $supported_db_version. Requested version: $db_version\n" + if $db_version > 0 and $db_version != $supported_db_version; + +die "ERROR: " . sourcetraildb::getLastError unless sourcetraildb::open($database_file_path); + +say "Clearing loaded database now..."; +sourcetraildb::clear; + +say "start indexing"; +sourcetraildb::beginTransaction; + +my $file_id = sourcetraildb::recordFile($source_file_path); +sourcetraildb::recordFileLanguage( $file_id, 'perl' ); +die "ERROR: " . sourcetraildb::getLastError if sourcetraildb::getLastError; + +my $symbol_id = sourcetraildb::recordSymbol( + '{ "name_delimiter": "::", "name_elements": [ { "prefix": "", "name": "MyClass", "postfix": "" } ] }'); +sourcetraildb::recordSymbolDefinitionKind( $symbol_id, $sourcetraildb::DEFINITION_EXPLICIT ); +sourcetraildb::recordSymbolKind( $symbol_id, $sourcetraildb::SYMBOL_PACKAGE ); +sourcetraildb::recordSymbolLocation( $symbol_id, $file_id, 1, 9, 1, 15 ); +sourcetraildb::recordSymbolScopeLocation( $symbol_id, $file_id, 1, 1, 7, 2 ); + +my $member_id + = sourcetraildb::recordSymbol( '{ "name_delimiter": "::", "name_elements": [ ' + . '{ "prefix": "", "name": "MyClass", "postfix": "" }, ' + . '{ "prefix": "$", "name": "my_member", "postfix": "" } ' + . '] }' ); +sourcetraildb::recordSymbolDefinitionKind( $member_id, $sourcetraildb::DEFINITION_EXPLICIT ); +sourcetraildb::recordSymbolKind( $member_id, $sourcetraildb::SYMBOL_FIELD ); +sourcetraildb::recordSymbolLocation( $member_id, $file_id, 3, 4, 3, 13 ); + +my $method_id + = sourcetraildb::recordSymbol( '{ "name_delimiter": "::", "name_elements": [ ' + . '{ "prefix": "", "name": "MyClass", "postfix": "" }, ' + . '{ "prefix": "&", "name": "my_method", "postfix": "" } ' + . '] }' ); +sourcetraildb::recordSymbolDefinitionKind( $method_id, $sourcetraildb::DEFINITION_EXPLICIT ); +sourcetraildb::recordSymbolKind( $method_id, $sourcetraildb::SYMBOL_METHOD ); +sourcetraildb::recordSymbolLocation( $method_id, $file_id, 5, 5, 5, 13 ); +sourcetraildb::recordSymbolScopeLocation( $method_id, $file_id, 6, 1, 7, 1 ); + +my $usage_id = sourcetraildb::recordReference( $method_id, $member_id, $sourcetraildb::REFERENCE_USAGE ); +sourcetraildb::recordReferenceLocation( $usage_id, $file_id, 6, 9, 6, 18 ); + +sourcetraildb::commitTransaction; +die "ERROR: " . sourcetraildb::getLastError if sourcetraildb::getLastError; +die "ERROR: " . sourcetraildb::getLastError unless sourcetraildb::close; + +say "done";