preface

This article introduces the NDK tool library for cross-compiling C/C++ and building Android projects using Makefiles and cmake.

The compiler

Understand the basic use of c/ C ++ compilers and be able to clearly understand what parameters should be passed when porting third-party frameworks for cross-compilation.

1. clang

Clang is a lightweight compiler for C, C++, and Object-C. Based on LLVM (LLVM is written by C++ framework compiler framework system, can be said to be a compiler related library for development), compared with GCC, it has the advantages of faster compilation speed, compilation output is smaller, but some software in the use of clang compilation because of the content of the source code will appear errors.

2. gcc

GNU C compiler. Originally designed to handle only C, it quickly expanded to handle C++. (GNU aims to create a completely free operating system).

3. g++

The GNU c++ compiler, a.c source file that GCC treats as a c program and g++ as a c++ program; G++ automatically links to the c++ library STL. GCC does not define __cplusplus macros. G++ does.

Compilation principle

A C/C++ file needs to be preprocessing, compilation, assembly, and linking to become executable.

We first create a test.c file on the Linux system, write a simplest C program, the code is as follows:

#include<stdio.h>
int main(a){
	printf("Execution successful! \n");
return 19921001;
}
Copy the code
  1. Pretreatment stage

    In the pre-processing stage, include and define are processed. It inserts the.h file included with #include into the #include location, and replaces the macro defined with the actual string used in the source program.

    To preprocess c/ C ++ files, run the following command:

    GCC -e test.c -o test. I // -e stops compiling GCC after preprocessingCopy the code

    You can see that a test. I file is generated after entering this command.

  2. Compilation phase

    In this phase, GCC first checks the code for normality, syntax errors, and so on to determine what the code is actually doing.

    We can process the test. I file and compile it into an assembly file with the following command:

    gcc -S test.i -o test.s// -s generates assembler files after compiling.
    Copy the code

  3. Assembly stage

    The assembly stage translates.s files into binary machine instruction files. O. This stage receives.c,.i,.s files without problem.

    The binary machine instruction file.o is generated by using the following command:

    gcc -c test.s -o test.o
    Copy the code

  4. Link phase

    Link phase, link is the function library. You can implement this by running the following command:

    gcc -C test.o -o test
    	./test
    Copy the code

    Finally, we have a certain understanding of the compilation through the actual operation, of course, you can also directly through the following command:

gcc test.c -o test
Copy the code

So far we have successfully generated an executable file on Linux. Can we copy this executable file to an Android phone? Push the test executable to /data/local/ TMP as follows:

You can see the test in the mobile phone/data/local/TMP directory is readable to write executable permissions, but the execution is not successful, why is this? In fact, the main reason is that the CPU instruction set of the two platforms is not the same, there is no way to recognize instructions. So how to solve this problem? The Android NDK tool kit is used to cross-compile C/C++ code.

cross-compilation

Simply put, cross-compilation is when a program is compiled in a different environment than it actually runs in. That is, it generates executable code on one platform and on another.

Understanding cross-compilation is essential in audio and video development because third-party libraries need to be cross-compiled regardless of the mobile platform. Let’s use the previous example to show you how to cross-compile executable code for mobile platforms in Linux.

Understand the NDK

The Android native development kit (NDK) can be used for C++ development on the Android platform. NDK is not only a single-function tool, but also a comprehensive set of tools including apis, cross-compilers, debuggers, build tools, etc.

The following is a rough list of commonly used components.

  • ARM cross compiler
  • Build tools
  • Java native to oral files
  • C library
  • Math library
  • Smallest C++ library
  • ZLib compression library
  • POSIX threads
  • The Android logging library
  • Android native application API
  • OpenGL ES library
  • OpenSL ES library

Here’s a look at the NDK and directory structure provided by Android.

  • Ndk-build: This Shell script is the starting point of the Android NDK build system. It is usually executed in a project only with this command to compile the corresponding dynamic link library.
  • Nk-gdb: This Shell script allows Native code to be debugged with the GUN debugger and can be configured into AS to debug Native code AS Java code.
  • Nk-stack: This Shell script helps analyze the stack information when Native code crashes.
  • Build: This directory contains all the modules of the NDK build system.
  • Platforms: This directory contains headers and libraries that support different Android target versions. NDK build systems will reference headers and libraries on each platform depending on the configuration.
  • Toolchains: This directory contains cross-compilers for different platforms currently supported by the NDK – ARM, X86, MIPS, ARM is the most commonly used. The build system selects different cross-compilers depending on the configuration.

Let’s configure the cross-compiled environment variables

Environment Variable Configuration

  • NDK environment variable configuration on Linux:

    //1. vim /etc/profile#NDK environment variablesexport NDK_HOME=/root/android/ndk/android-ndk-r17c
    export PATH=$PATH:$NDK_HOME
    
    / / 2. Save
    source  /etc/profile
    
    / / 3. Test
    ndk-build -v
    Copy the code

    If the following information is displayed, the configuration is successful.

  • Cross-compile environment variable configuration on Linux (for reference, environment configuration after mining):

    export NDK_GCC_x86="/ root/android/the NDK/android - the NDK - r17c/toolchains/prebuilt/Linux/x86-4.9 - x86_64 / bin/i686 - Linux - android - GCC"
    export NDK_GCC_x64="/ root/android/the NDK/android - the NDK - r17c/toolchains/x86_64-4.9 / prebuilt/Linux - x86_64 / bin/x86_64 - Linux - android - GCC"
    export NDK_GCC_arm="/ root/android/the NDK/android - the NDK - r17c/toolchains/arm - Linux - androideabi - 4.9 / prebuilt/Linux - x86_64 / bin/arm - Linux - androideabi -gcc"
    export NDK_GCC_arm_64="/ root/android/the NDK/android - the NDK - r17c/toolchains/aarch64 - Linux - android 4.9 / prebuilt/Linux - x86_64 / bin/aarch64 - Linux - android -gcc"
    
    export NDK_CFIG_x86="--sysroot=/root/android/ndk/android-ndk-r17c/platforms/android-21/arch-x86 -isystem /root/android/ndk/android-ndk-r17c/sysroot/usr/include -isystem /root/android/ndk/android-ndk-r17c/sysroot/usr/include/i686-linux-android"
    export NDK_CFIG_x64="--sysroot=/root/android/ndk/android-ndk-r17c/platforms/android-21/arch-x86_64 -isystem /root/android/ndk/android-ndk-r17c/sysroot/usr/include -isystem /root/android/ndk/android-ndk-r17c/sysroot/usr/include/x86_64-linux-android"
    export NDK_CFIG_arm="--sysroot=/root/android/ndk/android-ndk-r17c/platforms/android-21/arch-arm -isystem /root/android/ndk/android-ndk-r17c/sysroot/usr/include -isystem /root/android/ndk/android-ndk-r17c/sysroot/usr/include/arm-linux-androideabi"
    export NDK_CFIG_arm_64="--isysroot=/root/android/ndk/android-ndk-r17c/platforms/android-21/arch-arm64 -isystem /root/android/ndk/android-ndk-r17c/sysroot/usr/include -isystem -isystem /root/android/ndk/android-ndk-r17c/sysroot/usr/include/aarch64-linux-android"
    
    export NDK_AR_x86="/ root/android/the NDK/android - the NDK - r17c/toolchains/prebuilt/Linux/x86-4.9 - x86_64 / bin/i686 - Linux - android - ar"
    export NDK_AR_x64="/ root/android/the NDK/android - the NDK - r17c/toolchains/aarch64 - Linux - android 4.9 / prebuilt/Linux - x86_64 / bin/aarch64 - Linux - android -ar"
    export NDK_AR_arm="/ root/android/the NDK/android - the NDK - r17c/toolchains/arm - Linux - androideabi - 4.9 / prebuilt/Linux - x86_64 / bin/arm - Linux - androideabi -ar"
    export NDK_AR_arm_64="/ root/android/the NDK/android - the NDK - r17c/toolchains/aarch64 - Linux - android 4.9 / prebuilt/Linux - x86_64 / bin/aarch64 - Linux - android -ar"
    
    Copy the code

    You can configure it according to your own NDK path corresponding to my environment variable. Test.c = test.c; test.c = test.c; test.c = test.c;

    1. First find / root/android/the NDK/android – the NDK – r17c/toolchains/arm – Linux – androideabi – 4.9 / prebuilt/Linux – x86_64 / bin/arm – Linux – androideabi – gcc

      Run the following command:

      / root/android/the NDK/android - the NDK - r17c/toolchains/arm - Linux - androideabi - 4.9 / prebuilt/Linux - x86_64 / bin/arm - Linux - androideabi - gcc -o test test.cCopy the code

      This error means that the compiler cannot find the stdio.h header file we introduced when we were compiling it. How to specify these error headers

    2. Specifies the header code

      / root/android/the NDK/android - the NDK - r17c/toolchains/arm - Linux - androideabi - 4.9 / prebuilt/Linux - x86_64 / bin/arm - Linux - androideabi - gcc --sysroot=/root/android/ndk/android-ndk-r17c/platforms/android-21/arch-arm -isystem /root/android/ndk/android-ndk-r17c/sysroot/usr/include -pie -otest test.c
      Copy the code

      There are several command symbols in the command. If you do not understand it, you can see the following explanation:

      –sysroot=? : use? As a lookup directory for the header and library files compiled this time, look for the following usr/include directory.

      -isystem ? Use header file to find directory, overwrite –sysroot, find? A header file under the /usr/include directory.

      -isystem ? ** Specifies the search path for the header file.

      -I? : search directory for the header file. I is uppercase.

      We will still report an ASM /types.h file that cannot be found after compiling. We will need to change the path as follows

      / root/android/the NDK/android - the NDK - r17c/toolchains/arm - Linux - androideabi - 4.9 / prebuilt/Linux - x86_64 / bin/arm - Linux - androideabi - gcc --sysroot=/root/android/ndk/android-ndk-r17c/platforms/android-21/arch-arm -isystem /root/android/ndk/android-ndk-r17c/sysroot/usr/include -isystem /root/android/ndk/android-ndk-r17c/sysroot/usr/include/arm-linux-androideabi -pie -otest test.c
      Copy the code

      This can be compiled into an executable file for Android platform, so it looks like too many paths are not easy to read, you can refer to the global variable configuration PROVIDED by me to set, the last line of command to solve, as follows:

      $NDK_GCC_arm $NDK_CFIG_arm -pie -o test test.c
      Copy the code

      As you can see, the executable we compiled using the Android NDK is no longer executable on Linux. /data/local/ TMP

    3. Import the NDK cross-compiled test executable file into the Android phone and execute the test file.

      According to the above screen recording, we know that we have successfully executed NDK cross-compiled test file on the Android device.

    Let’s use the NDK tool to cross-compile test.c to output static and dynamic libraries.

Dynamic library & static library

Compiling a static library

  1. To compile test.c as a.o file using NDK GCC:

    $NDK_GCC_arm $NDK_CFIG_arm -fpic -c test.c -o test.o
    Copy the code

    If the following file appears, you have succeeded.

  2. Run the NDK arm-linux-androideabi-ar tool to generate test.o file into test.a static library:

    $NDK_AR_arm r test.a test.o
    Copy the code

    Then we import the test.a file into AS to use.a.

Compiling dynamic libraries

We need to specify -fpic-shared as an additional parameter to the compiler when compiling the dynamic library.

$NDK_GCC_arm $NDK_CFIG_arm -fpic -shared test.c -o libTest.so
Copy the code

The difference between dynamic and static libraries

In daily work, we often package some commonly used functions or functions into one library for others to use. Java development can be packaged into JA R package for others to use, and Android platform can be packaged into AAR package later. Similarly, C/C++ features or functions that we encapsulate can be made available to others through static or dynamic libraries.

Linux platform static libraries end in. A, while dynamic libraries end in. So.

What’s the difference between a static library and a dynamic library?

1. Static library

When connected to a static library, the machine code for all functions used in the static library is copied to the final executable at compile time and added to every program connected to it:

Advantages: Run faster, no need to search the rest of the file library.

Disadvantages: The resulting amount of executable code is relatively large, which is loaded into memory at run time. It consumes more memory space.

2. Dynamic libraries

The executable file attached to the dynamic library contains only reference tables of the required functions, not all of the function code, which is copied into memory only when the program is executed.

Advantages: The generation of executable files is small, saving disk space. A dynamic library resides in memory and is used by multiple programs, which also saves memory.

Disadvantages: Execution is relatively slow due to the time it takes for the runtime to go to the linked library.

Static library is time for space, dynamic library is space for time, both are good and bad.

If we want to change the function library, the program using the dynamic library only needs to recompile the dynamic library, while the program using the static library needs to recompile the static library, the program will be recompiled again.

mk & cmake

Build a c /C++ Android program based on makefile and cmake, and use test. a/libtest. so

mk

Android. Mk is a Makefile that builds an application system written in C or C ++ on the Android platform. The difference is that Android provides a number of built-in variables to provide more convenient build syntax rules. The application.mk file is actually a description of the Application itself, describing which CPU architectures the Application packages dynamic SO against, whether a release or debug package is to be built, and some compilation and linking parameters.

Grammar foundation

1. Android.mk

  • LOCAL_PATH :=$(call my-dir)

    Returns the path of the current file in the system. This variable must be defined at the beginning of the Android.mk file.

  • Include $(CLEAR_VARS) specifies that all global variables from the previous build process are clear. Since a large number of global variables are used in a Makefile compilation script, using this script indicates that all global variables need to be removed.

  • LOCAL_SRC_FILES, C or CPP files to compile, note that there is no need to enumerate header files, the build system automatically helps the developer to rely on these files.

  • LOCAL_LDLIBS:= -l $Specifies the supplied dynamic static library on which the compilation process depends, /usr/ lib-ilog-iOpenSLes-iglesv2-iegl-iz/SYSROOT /usr/ lib-ilog-iOpenSLes-iglesv2-iegl-iz The SYSROOT variable represents the dynamic and static libraries provided by $NDK under NDK_ROOT. The SYSROOT variable represents the NDK_ROOT/platforms/android-21/arch-arm directory, and in this directory there are many corresponding dynamic libraries for so and static libraries for.a.

  • LOCAL_CFLAGS, the compiler flag for compiling C or CPP, is sent to the compiler at actual compilation time. A common example is to add -dauto_test, and then use the conditional #ifdef AUTO_TEST to do something related to automated testing in your code.

  • LOCAL_LDFLAGS, an optional list of link flags to be brought to the linker when linking to an object file to generate an output file. This directive is similar to LOCAL_LDLIBS, which is typically used to specify static libraries compiled by third parties. LOCAL_LDLIBS is often used to specify system libraries (such as log, OpenGLES, EGL, etc.).

  • LOCAL_MODULE, the compile target name of the module, used to distinguish modules. The name must be unique without Spaces. If the compile target is the so library, the name of the so library is the lib project name.

  • Include $(BUILD_SHARED_LIBRARY); include $(BUILD_SHARED_LIBRARY); include $(BUILD_SHARED_LIBRARY);

    • –BUILD_STATIC_LIBRARY: Builds the static library
    • –PREBUILT_STATIC_LIBRARY: Wraps an existing static library into a module.
    • –PREBUILT_SHARED_LIBRARY: Wraps an existing static library into a module.
    • –BUILD_EXECUTABLE: builds the executable file.

2. Application.mk

  • APP_ABI := XXX, where XXX refers to different platforms, which can be selected as x86, MIPS, armeabi, armeabi-v7A, all, etc. It is worth saying that if you select all, so will be built for all platforms. If you do not fill in this parameter, Build the library under armeabi by default.
  • APP_STL := gnustl_static,NDK build system provides the minimum C++ runtime library provided by Android (system/lib/libstdc++ so) C++ header file.
  • APP_CPPFLAGS :=-std=gnu++11 -fexceptions specifies flags for the compilation process. Features such as exception rtti can be turned on in this option, but rtti is best turned off for efficiency.
  • NDK_TOOLCHAIN_VERSION = 4.8, specify the version number in the cross tool build chain, specify 4.8 here.
  • APP_PLATFORM :=android-9, which specifies the platform to create the dynamic library
  • APP_OPTIM := release, which is optional and defines “release” or “debug”. The “release” mode is default and generates highly optimized binaries; The “debug” mode generates unoptimized binary code, but can detect a lot of bugs. It is often used during debugging, and is equivalent to adding NDK_DEBUG=1 to the ndK-build directive.

Build a C/C++ Android project

Project source code

Effect:

We will only do a brief understanding of Makefile, because we will use Cmake to build C/C++ Android projects in the future, so we will focus on mastering Cmake.

cmake

The previous NDK development or old projects were built based on Android.mk and Application.mk, but since AS 2.2, I began to use CMake to build C/C++ projects. Using CMake compared with the previous Android. Mk, Application. Mk is much more convenient and simple. Let’s take a look at the basic syntax of Cmake.

Grammar foundation

#1. Specify the minimum version of cmake
cmake_minimum_required(VERSION 3.4.1)

#2. Set the project name
project(demo)

#3. Set the compile type
add_executable(demo test.cpp) Generate an executable file
add_library(common STATIC test.cpp) Generate static libraries
add_library(common SHARED test.cpp) Create dynamic libraries or shared libraries

#4. Specify explicitly which source files to include
add_library(demo test.cpp test1.cpp test2.cpp)

#5. Customize search rules and load files
file(GLOB SRC_LIST "*.cpp" "protocol/*.cpp")
add_library(demo ${SRC_LIST}// Load all CPP files in the current directory# # or
file(GLOB SRC_LIST "*.cpp")
file(GLOB SRC_PROTOCOL_LIST "protocol/*.cpp")
add_library(demo ${SRC_LIST} ${SRC_PROTOCOL_LIST})
# # or
aux_source_directory(.src_list)// Search for all. CPP files in the current directoryaux_source_directory(protocol SRC_PROTOCOL_LIST) 
add_library(demo ${SRC_LIST} ${SRC_PROTOCOL_LIST})

#6. Find the specified library file
find_library(log-lib // define a variable name for log) // log library under NDK#7. Set the included directory
include_directories(
    ${CMAKE_CURRENT_SOURCE_DIR}
    ${CMAKE_CURRENT_BINARY_DIR}
    ${CMAKE_CURRENT_SOURCE_DIR}/include
)

#8. Set up the link library search directory
link_directories(
    ${CMAKE_CURRENT_SOURCE_DIR}/libs
)

#9. Set the library that target needs to link to
target_link_libraries( # object library
                       demo
 
                       The target library needs a linked library
                       # log-lib is the variable name specified by find_library above
                       ${log-lib} )
                       
#10. Specify link dynamic or static libraries
target_link_libraries(demo libtest.a) # link libtest. A
target_link_libraries(demo libtest.so) # link libtest. So

#11. Link dynamic static libraries according to the full path
target_link_libraries(demo ${CMAKE_CURRENT_SOURCE_DIR}/libs/libtest.a)
target_link_libraries(demo ${CMAKE_CURRENT_SOURCE_DIR}/libs/libtest.so)

#12. Specify that multiple libraries are linked
target_link_libraries(demo
    ${CMAKE_CURRENT_SOURCE_DIR}/libs/libtest.a
    test.a
    boost_thread
    pthread)
    
Copy the code

Commonly used variables

Predefined variable instructions
PROJECT_SOURCE_DIR The root directory of the project
PROJECT_BINARY_DIR The directory to run the cmake command is typically ${PROJECT_SOURCE_DIR}/build
PROJECT_NAME Returns the project name defined by the project command
CMAKE_CURRENT_SOURCE_DIR Path where cmakelists.txt is currently processed
CMAKE_CURRENT_BINARY_DIR Target Compile directory
CMAKE_CURRENT_LIST_DIR The full path to cmakelists.txt
CMAKE_CURRENT_LIST_LINE The current row
CMAKE_MODULE_PATH Define the path of your own cmake module, SET(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake), and then call your own module with INCLUDE
EXECUTABLE_OUTPUT_PATH Redefines the location where the target binary executable is stored
LIBRARY_OUTPUT_PATH Redefine the location of the target link library file

Build a C/C++ Android project

  1. Build projects with static libraries

    • Defining a native interface

      public class MainActivity extends AppCompatActivity {
      
          static {
              System.loadLibrary("native-lib");
          }
      
          @Override
          protected void onCreate(Bundle savedInstanceState) {
              super.onCreate(savedInstanceState);
              setContentView(R.layout.activity_main);
              testCmake();
          }
      
          /** * Test the cmake builder */
          public native static void testCmake(a);
      }
      Copy the code
    • Write the CPP

      // extern int main(); This is a pit because the main method belongs to C, and it's currently CPP extern"C"{// we must define int main() like this; } extern"C" JNIEXPORT void JNICALL
      Java_com_devyk_cmake_1application_MainActivity_testCmake(
              JNIEnv *env,
              jobject /* this */) {
          std::string hello = "Hello from C++";
      
          __android_log_print(ANDROID_LOG_DEBUG, "DevYK"."main--->:%d", main());
      
      }
      Copy the code
    • Compile the cmakelists.txt file

      cmake_minimum_required(VERSION 3.4.1)
      
      # print log
      message(${CMAKE_SOURCE_DIR} ${CMAKE_SOURCE_DIR})
      message("The current path to CMAKE_ANDROID_ARCH_ABI is ${CMAKE_ANDROID_ARCH_ABI}")
      
      # Batch import source files
      file(GLOB allCpp *.cpp)
      
      Add CPP source files
      add_library(
              native-lib
              SHARED
              # native-lib. CPP replace ${allCpp} with batch import files
              ${allCpp}
      )
      
      # import static library
      add_library(test_a STATIC IMPORTED)
      # Start the actual import
      set_target_properties(test_a PROPERTIES IMPORTED_LOCATION ${CMAKE_SOURCE_DIR}/libtest.a)
      
      # System only
      find_library(
              log-lib
              log)
              
      message(Where is the current log path >>>>>>>>>>>>>>>>> ${log-lib})
      
      # start linking to the specified library
      target_link_libraries(
              native-lib
              ${log-lib}
              test_a
      )
      Copy the code
    • App/build. Gradle cmake configuration

      android {
      ...
      
      
          defaultConfig {
      ...
      
              externalNativeBuild {
                  cmake {
                      // cppFlags "" // Contains four major platforms by default
                      abiFilters 'armeabi-v7a'// Build armeabi-v7A platform
                  }
              }
      
      
              ndk {
                  // Only use this version of the library, otherwise the default is 4 platforms
                  abiFilters 'armeabi-v7a'}}... externalNativeBuild { cmake { path"src/main/cpp/CMakeLists.txt" // Specify the CMakeLists path}}}Copy the code
    • The test results

  2. Build projects with dynamic libraries

    • The code loads the so library into the phone

          static {
              System.loadLibrary("Test");
              System.loadLibrary("native-lib");
          }
      Copy the code
    • So library is imported under main/jniLibs

    • CmakeLists. TXT configuration

      cmake_minimum_required(VERSION 3.4.1)
      
      # print log
      message("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA>>>")
      message(${CMAKE_SOURCE_DIR} ${CMAKE_SOURCE_DIR})
      message("The current path to CMAKE_ANDROID_ARCH_ABI is ${CMAKE_ANDROID_ARCH_ABI}")
      
      # Batch import source files
      file(GLOB allCpp *.cpp)
      
      Add CPP source files
      add_library(
              native-lib
              SHARED
              # native-lib.cpp
              ${allCpp}
      )
      
      # import static library
      #add_library(test_a STATIC IMPORTED)
      # Start the actual import
      #set_target_properties(test_a PROPERTIES IMPORTED_LOCATION ${CMAKE_SOURCE_DIR}/libtest.a)
      
      # import dynamic library
      add_library(test_so SHARED IMPORTED)
      # early cmake ANDROID_ABI == current CPU platform
      set_target_properties(test_so PROPERTIES IMPORTED_LOCATION ${CMAKE_SOURCE_DIR}/.. /jniLibs/${CMAKE_ANDROID_ARCH_ABI}/libTest.so)
      
      # System only
      find_library(
              log-lib
              log)
      message(Where is the current log path >>>>>>>>>>>>>>>>> ${log-lib})
      
      # CMAKE_SOURCE_DIR == D:\NDK\CoursewareCreate\ndk_12\project\ndk12_cmake\app\src\main\cpp\CMakeLists.txt
      
      target_link_libraries(
              native-lib
              ${log-lib}
              test_so
      )
      Copy the code
    • test

Here, MK and Cmake entry basic knowledge is finished, want to master all need to do more hands-on practice.

conclusion

This article mainly explains how to use NDK to cross-compile C programs, and the use of cross-compiled dynamic and static libraries in Android projects, as well as the use of Makefile and cmake in Android, this article is also relatively basic. Lay the foundation for future use or compilation of FFmpeg.

All the code in this article has been uploaded to GitHub repository