mirror of
https://github.com/cookiengineer/audacity
synced 2025-08-01 00:19:27 +02:00
Remove gettext download for Mac in CMake build
And added a python version that will be used on all platforms when a suitable msgfmt isn't installed. (As long as python is installed.) Extracted all the Audacity specific functions from main cmake list and moved them to their own module. Rearrange the main cmake module a bit and misc. cleanup.
This commit is contained in:
parent
fe09b0c4ac
commit
caab2a56c9
319
CMakeLists.txt
319
CMakeLists.txt
@ -19,6 +19,10 @@ if( "${CMAKE_SOURCE_DIR}" STREQUAL "${CMAKE_BINARY_DIR}" )
|
||||
)
|
||||
endif()
|
||||
|
||||
# Just a couple of convenience variables
|
||||
set( topdir "${CMAKE_SOURCE_DIR}" )
|
||||
set( libsrc "${topdir}/lib-src" )
|
||||
|
||||
# Ignore COMPILE_DEFINITIONS_<Config> properties
|
||||
cmake_policy( SET CMP0043 NEW )
|
||||
|
||||
@ -73,39 +77,8 @@ endif()
|
||||
# Our very own project
|
||||
project( Audacity )
|
||||
|
||||
message( STATUS "Build Info:" )
|
||||
message( STATUS " Host System: ${CMAKE_HOST_SYSTEM}" )
|
||||
message( STATUS " Host System Name: ${CMAKE_HOST_SYSTEM_NAME}" )
|
||||
message( STATUS " Host System Processor: ${CMAKE_HOST_SYSTEM_PROCESSOR}" )
|
||||
message( STATUS " Host System Version: ${CMAKE_HOST_SYSTEM_VERSION}" )
|
||||
message( STATUS )
|
||||
message( STATUS " Compiler: ${CMAKE_CXX_COMPILER}" )
|
||||
message( STATUS " Compiler Standard: ${CMAKE_CXX_STANDARD}" )
|
||||
message( STATUS " Compiler Standard Required: ${CMAKE_CXX_STANDARD_REQUIRED}" )
|
||||
message( STATUS " Compiler Extensions: ${CMAKE_CXX_EXTENSIONS}" )
|
||||
message( STATUS )
|
||||
if( APPLE )
|
||||
message( STATUS " Xcode Version: ${XCODE_VERSION}" )
|
||||
message( STATUS " MacOS SDK: ${CMAKE_OSX_SYSROOT}" )
|
||||
message( STATUS )
|
||||
endif()
|
||||
|
||||
# Define option() prefix
|
||||
set( _OPT "audacity_" )
|
||||
|
||||
# Try to get the current commit hash
|
||||
find_package( Git QUIET )
|
||||
if( GIT_FOUND )
|
||||
execute_process(
|
||||
COMMAND
|
||||
${GIT_EXECUTABLE} show -s --format='%h'
|
||||
OUTPUT_VARIABLE
|
||||
short_hash
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
)
|
||||
message( STATUS " Current Commit: ${short_hash}" )
|
||||
message( STATUS )
|
||||
endif()
|
||||
# Load our functions/macros
|
||||
include( AudacityFunctions )
|
||||
|
||||
# Pull all the modules we'll need
|
||||
include( CheckCXXCompilerFlag )
|
||||
@ -120,6 +93,55 @@ include( CMakePushCheckState )
|
||||
include( GNUInstallDirs )
|
||||
include( TestBigEndian )
|
||||
|
||||
message( STATUS "Build Info:" )
|
||||
message( STATUS " Host System: ${CMAKE_HOST_SYSTEM}" )
|
||||
message( STATUS " Host System Name: ${CMAKE_HOST_SYSTEM_NAME}" )
|
||||
message( STATUS " Host System Processor: ${CMAKE_HOST_SYSTEM_PROCESSOR}" )
|
||||
message( STATUS " Host System Version: ${CMAKE_HOST_SYSTEM_VERSION}" )
|
||||
message( STATUS )
|
||||
message( STATUS " Compiler: ${CMAKE_CXX_COMPILER}" )
|
||||
message( STATUS " Compiler Version: ${CMAKE_CXX_COMPILER_VERSION}" )
|
||||
message( STATUS " Compiler Standard: ${CMAKE_CXX_STANDARD}" )
|
||||
message( STATUS " Compiler Standard Required: ${CMAKE_CXX_STANDARD_REQUIRED}" )
|
||||
message( STATUS " Compiler Extensions: ${CMAKE_CXX_EXTENSIONS}" )
|
||||
message( STATUS )
|
||||
|
||||
if( CMAKE_GENERATOR MATCHES "Visual Studio" )
|
||||
message( STATUS " MSVC Version: ${MSVC_VERSION}" )
|
||||
message( STATUS " MSVC Toolset: ${MSVC_TOOLSET_VERSION}" )
|
||||
message( STATUS )
|
||||
elseif( CMAKE_SYSTEM_NAME MATCHES "Darwin" )
|
||||
if( CMAKE_GENERATOR MATCHES "Visual Studio" )
|
||||
message( STATUS " Xcode Version: ${XCODE_VERSION}" )
|
||||
endif()
|
||||
message( STATUS " MacOS SDK: ${CMAKE_OSX_SYSROOT}" )
|
||||
message( STATUS )
|
||||
endif()
|
||||
|
||||
# Try to get the current commit information
|
||||
find_package( Git QUIET )
|
||||
if( GIT_FOUND )
|
||||
execute_process(
|
||||
COMMAND
|
||||
${GIT_EXECUTABLE} show -s "--format=%h;%H;%cd"
|
||||
WORKING_DIRECTORY
|
||||
${topdir}
|
||||
OUTPUT_VARIABLE
|
||||
output
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
)
|
||||
|
||||
list( GET output 0 GIT_COMMIT_SHORT )
|
||||
list( GET output 1 GIT_COMMIT_LONG )
|
||||
list( GET output 2 GIT_COMMIT_TIME )
|
||||
|
||||
message( STATUS " Current Commit: ${GIT_COMMIT_SHORT}" )
|
||||
message( STATUS )
|
||||
endif()
|
||||
|
||||
# Define option() prefix
|
||||
set( _OPT "audacity_" )
|
||||
|
||||
# Organize subdirectories/targets into folders for the IDEs
|
||||
set_property( GLOBAL PROPERTY USE_FOLDERS ON )
|
||||
|
||||
@ -158,14 +180,10 @@ set( CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE )
|
||||
set( CMAKE_MACOSX_RPATH FALSE )
|
||||
|
||||
# the RPATH to be used when installing, but only if it's not a system directory
|
||||
#list( FIND CMAKE_PLATFORM_IMPLICIT_LINK_DIRECTORIES "${CMAKE_INSTALL_FULL_LIBDIR}" isSystemDir)
|
||||
#IF("${isSystemDir}" STREQUAL "-1")
|
||||
# SET(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/lib")
|
||||
#ENDIF("${isSystemDir}" STREQUAL "-1")
|
||||
|
||||
# Just a couple of convenience variables
|
||||
set( topdir "${CMAKE_SOURCE_DIR}" )
|
||||
set( libsrc "${topdir}/lib-src" )
|
||||
#list( FIND CMAKE_PLATFORM_IMPLICIT_LINK_DIRECTORIES "${CMAKE_INSTALL_FULL_LIBDIR}" isSysDir )
|
||||
#if( "${isSysDir}" STREQUAL "-1" )
|
||||
# set( CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/lib" )
|
||||
#endif()
|
||||
|
||||
# Add the math library (if found) to the list of required libraries
|
||||
check_library_exists( m pow "" HAVE_LIBM )
|
||||
@ -222,14 +240,12 @@ endif()
|
||||
|
||||
check_include_files( "float.h;stdarg.h;stdlib.h;string.h" STDC_HEADERS )
|
||||
|
||||
check_include_file( "alloca.h" HAVE_ALLOCA_H )
|
||||
check_include_file( "assert.h" HAVE_ASSERT_H )
|
||||
check_include_file( "byteswap.h" HAVE_BYTESWAP_H )
|
||||
check_include_file( "errno.h" HAVE_ERRNO_H )
|
||||
check_include_file( "fcntl.h" HAVE_FCNTL_H )
|
||||
check_include_file( "fenv.h" HAVE_FENV_H )
|
||||
check_include_file( "inttypes.h" HAVE_INTTYPES_H )
|
||||
check_include_file( "libudev.h" HAVE_LIBUDEV_H )
|
||||
check_include_file( "limits.h" HAVE_LIMITS_H )
|
||||
check_include_file( "malloc.h" HAVE_MALLOC_H )
|
||||
check_include_file( "memory.h" HAVE_MEMORY_H )
|
||||
@ -313,14 +329,6 @@ find_package( PkgConfig QUIET )
|
||||
# Mostly just to make the CMP0072 policy happy
|
||||
find_package( OpenGL QUIET )
|
||||
|
||||
# Defines several useful directory paths for the active context.
|
||||
macro( def_vars )
|
||||
set( _SRCDIR "${CMAKE_CURRENT_SOURCE_DIR}" )
|
||||
set( _INTDIR "${CMAKE_CURRENT_BINARY_DIR}" )
|
||||
set( _PRVDIR "${CMAKE_CURRENT_BINARY_DIR}/private" )
|
||||
set( _PUBDIR "${CMAKE_CURRENT_BINARY_DIR}/public" )
|
||||
endmacro()
|
||||
|
||||
# Define the non-install and executable destinations
|
||||
#
|
||||
# If this is a multi-config build system (VS, Xcode), CMAKE_CFG_INTDIR
|
||||
@ -391,200 +399,15 @@ else()
|
||||
set( AUDACITY_SUFFIX "" )
|
||||
endif()
|
||||
|
||||
# Extract the current commit information
|
||||
if (GIT_FOUND)
|
||||
execute_process(
|
||||
COMMAND
|
||||
${GIT_EXECUTABLE} show -s "--format=\"%H\";\"%cd\";"
|
||||
WORKING_DIRECTORY
|
||||
${topdir}
|
||||
OUTPUT_VARIABLE
|
||||
output
|
||||
)
|
||||
|
||||
list( GET output 0 long )
|
||||
list( GET output 1 time )
|
||||
list( APPEND DEFINES
|
||||
REV_LONG=${long}
|
||||
REV_TIME=${time}
|
||||
)
|
||||
# Python is used for the manual and (possibly) message catalogs
|
||||
find_package( Python2 )
|
||||
if( Python2_FOUND )
|
||||
set( PYTHON "${Python2_EXECUTABLE}" )
|
||||
elseif( CMAKE_SYSTEM_NAME MATCHES "Windows" )
|
||||
nuget_package( pkgdir "python2" "2.7.17" )
|
||||
file( TO_NATIVE_PATH "${pkgdir}/tools/python.exe" PYTHON )
|
||||
endif()
|
||||
|
||||
# Helper to organize sources into folders for the IDEs
|
||||
macro( organize_source root prefix sources )
|
||||
set( cleaned )
|
||||
foreach( source ${sources} )
|
||||
# Remove generator expressions
|
||||
string( REGEX REPLACE ".*>:(.*)>*" "\\1" source "${source}" )
|
||||
string( REPLACE ">" "" source "${source}" )
|
||||
|
||||
# Remove keywords
|
||||
string( REGEX REPLACE "^[A-Z]+$" "" source "${source}" )
|
||||
|
||||
# Add to cleaned
|
||||
list( APPEND cleaned "${source}" )
|
||||
endforeach()
|
||||
|
||||
# Define the source groups
|
||||
if( "${prefix}" STREQUAL "" )
|
||||
source_group( TREE "${root}" FILES ${cleaned} )
|
||||
else()
|
||||
source_group( TREE "${root}" PREFIX ${prefix} FILES ${cleaned} )
|
||||
endif()
|
||||
endmacro()
|
||||
|
||||
# Given a directory, recurse to all defined subdirectories and assign
|
||||
# the given folder name to all of the targets found.
|
||||
function( set_dir_folder dir folder)
|
||||
get_property( subdirs DIRECTORY "${dir}" PROPERTY SUBDIRECTORIES )
|
||||
foreach( sub ${subdirs} )
|
||||
set_dir_folder( "${sub}" "${folder}" )
|
||||
endforeach()
|
||||
|
||||
get_property( targets DIRECTORY "${dir}" PROPERTY BUILDSYSTEM_TARGETS )
|
||||
foreach( target ${targets} )
|
||||
get_target_property( type "${target}" TYPE )
|
||||
if( NOT "${type}" STREQUAL "INTERFACE_LIBRARY" )
|
||||
set_target_properties( ${target} PROPERTIES FOLDER ${folder} )
|
||||
endif()
|
||||
endforeach()
|
||||
endfunction()
|
||||
|
||||
# Helper to retrieve the settings returned from pkg_check_modules()
|
||||
macro( get_package_interface package )
|
||||
set( INCLUDES
|
||||
${${package}_INCLUDE_DIRS}
|
||||
)
|
||||
|
||||
set( LINKDIRS
|
||||
${${package}_LIBDIR}
|
||||
)
|
||||
|
||||
# We resolve the full path of each library to ensure the
|
||||
# correct one is referenced while linking
|
||||
foreach( lib ${${package}_LIBRARIES} )
|
||||
find_library( LIB_${lib} ${lib} HINTS ${LINKDIRS} )
|
||||
list( APPEND LIBRARIES ${LIB_${lib}} )
|
||||
endforeach()
|
||||
endmacro()
|
||||
|
||||
# Set the cache and context value
|
||||
macro( set_cache_value var value )
|
||||
set( ${var} "${value}" )
|
||||
set_property( CACHE ${var} PROPERTY VALUE "${value}" )
|
||||
endmacro()
|
||||
|
||||
# Set the given property and its config specific brethren to the same value
|
||||
function( set_target_property_all target property value )
|
||||
set_target_properties( "${target}" PROPERTIES "${property}" "${value}" )
|
||||
foreach( type ${CMAKE_CONFIGURATION_TYPES} )
|
||||
string( TOUPPER "${property}_${type}" prop )
|
||||
set_target_properties( "${target}" PROPERTIES "${prop}" "${value}" )
|
||||
endforeach()
|
||||
endfunction()
|
||||
|
||||
# Taken from wxWidgets and modified for Audcaity
|
||||
#
|
||||
# cmd_option(<name> <desc> [default] [STRINGS strings])
|
||||
# The default is ON if third parameter isn't specified
|
||||
function( cmd_option name desc )
|
||||
cmake_parse_arguments( OPTION "" "" "STRINGS" ${ARGN} )
|
||||
|
||||
if( ARGC EQUAL 2 )
|
||||
if( OPTION_STRINGS )
|
||||
list( GET OPTION_STRINGS 1 default )
|
||||
else()
|
||||
set( default ON )
|
||||
endif()
|
||||
else()
|
||||
set( default ${OPTION_UNPARSED_ARGUMENTS} )
|
||||
endif()
|
||||
|
||||
if( OPTION_STRINGS )
|
||||
set( cache_type STRING )
|
||||
else()
|
||||
set( cache_type BOOL )
|
||||
endif()
|
||||
|
||||
set( ${name} "${default}" CACHE ${cache_type} "${desc}" )
|
||||
if( OPTION_STRINGS )
|
||||
set_property( CACHE ${name} PROPERTY STRINGS ${OPTION_STRINGS} )
|
||||
|
||||
# Check valid value
|
||||
set( value_is_valid FALSE )
|
||||
set( avail_values )
|
||||
foreach( opt ${OPTION_STRINGS} )
|
||||
if( ${name} STREQUAL opt )
|
||||
set( value_is_valid TRUE )
|
||||
break()
|
||||
endif()
|
||||
string( APPEND avail_values " ${opt}" )
|
||||
endforeach()
|
||||
if( NOT value_is_valid )
|
||||
message( FATAL_ERROR "Invalid value \"${${name}}\" for option ${name}. Valid values are: ${avail_values}" )
|
||||
endif()
|
||||
endif()
|
||||
|
||||
set( ${name} "${${name}}" PARENT_SCOPE )
|
||||
endfunction()
|
||||
|
||||
# Downloads NuGet packages
|
||||
#
|
||||
# Why this is needed...
|
||||
#
|
||||
# To get NuGet to work, you have to add the VS_PACKAGE_REFERENCES
|
||||
# property to a target. This target must NOT be a UTILITY target,
|
||||
# which is what we use to compile the message catalogs and assemble
|
||||
# the manual. We could add that property to the Audacity target and
|
||||
# CMake would add the required nodes to the VS project. And when the
|
||||
# Audacity target is built, the NuGet packages would get automatically
|
||||
# downloaded. This also means that the locale and manual targets
|
||||
# must be dependent on the Audacity target so the packages would get
|
||||
# downloaded before they execute. This would be handled by the CMake
|
||||
# provided ALL_BUILD target which is, by default, set as the startup
|
||||
# project in Visual Studio. Sweet right? Well, not quite...
|
||||
#
|
||||
# We want the Audacity target to be the startup project to provide
|
||||
# eaiser debugging. But, if we do that, the ALL_BUILD target is no
|
||||
# longer "in control" and any dependents of the Audacity target would
|
||||
# not get built. So, targets like "nyquist" and "plug-ins" would have
|
||||
# to be manually built. This is not what we want since Nyquist would
|
||||
# not be available during Audacity debugging because the Nyquist runtime
|
||||
# would not be copied into the destination folder alonside the Audacity
|
||||
# executable.
|
||||
#
|
||||
# To remedy this conundrum, we simply download the NuGet packages
|
||||
# ourselves and make the Audacity target dependent on the targets
|
||||
# mentioned above. This ensures that the dest folder is populated
|
||||
# and laid out like Audacity expects.
|
||||
#
|
||||
function( nuget_package dir name version )
|
||||
# Generate the full package directory name
|
||||
set( pkgdir "${CMAKE_BINARY_DIR}/packages/${name}/${version}" )
|
||||
|
||||
# Don't download it again if the package directory already exists
|
||||
if( NOT EXISTS "${pkgdir}" )
|
||||
set( pkgurl "https://www.nuget.org/api/v2/package/${name}/${version}" )
|
||||
|
||||
# Create the package directory
|
||||
file( MAKE_DIRECTORY "${pkgdir}" )
|
||||
|
||||
# And download the package into the package directory
|
||||
file( DOWNLOAD "${pkgurl}" "${pkgdir}/package.zip" )
|
||||
|
||||
# Extract the contents of the package into the package directory
|
||||
execute_process(
|
||||
COMMAND
|
||||
${CMAKE_COMMAND} -E tar x "${pkgdir}/package.zip"
|
||||
WORKING_DIRECTORY
|
||||
${pkgdir}
|
||||
)
|
||||
endif()
|
||||
|
||||
# Return the package directory name to the caller
|
||||
set( ${dir} "${pkgdir}" PARENT_SCOPE )
|
||||
endfunction()
|
||||
|
||||
# Add our children
|
||||
add_subdirectory( "cmake-proxies" )
|
||||
add_subdirectory( "help" )
|
||||
@ -605,13 +428,13 @@ endif()
|
||||
|
||||
# Uncomment what follows for symbol values.
|
||||
#[[
|
||||
get_cmake_property(_variableNames VARIABLES)
|
||||
foreach (_variableName ${_variableNames})
|
||||
message(STATUS "${_variableName}=${${_variableName}}")
|
||||
get_cmake_property( _variableNames VARIABLES )
|
||||
foreach( _variableName ${_variableNames} )
|
||||
message( STATUS "${_variableName}=${${_variableName}}" )
|
||||
endforeach()
|
||||
#]]
|
||||
#[[
|
||||
include(PrintProperties)
|
||||
print_properties(TARGET "wxWidgets")
|
||||
include( PrintProperties )
|
||||
print_properties( TARGET "wxWidgets" )
|
||||
#]]
|
||||
|
||||
|
186
cmake-proxies/cmake-modules/AudacityFunctions.cmake
Normal file
186
cmake-proxies/cmake-modules/AudacityFunctions.cmake
Normal file
@ -0,0 +1,186 @@
|
||||
#
|
||||
# A collection of functions and macros
|
||||
#
|
||||
|
||||
# Defines several useful directory paths for the active context.
|
||||
macro( def_vars )
|
||||
set( _SRCDIR "${CMAKE_CURRENT_SOURCE_DIR}" )
|
||||
set( _INTDIR "${CMAKE_CURRENT_BINARY_DIR}" )
|
||||
set( _PRVDIR "${CMAKE_CURRENT_BINARY_DIR}/private" )
|
||||
set( _PUBDIR "${CMAKE_CURRENT_BINARY_DIR}/public" )
|
||||
endmacro()
|
||||
|
||||
# Helper to organize sources into folders for the IDEs
|
||||
macro( organize_source root prefix sources )
|
||||
set( cleaned )
|
||||
foreach( source ${sources} )
|
||||
# Remove generator expressions
|
||||
string( REGEX REPLACE ".*>:(.*)>*" "\\1" source "${source}" )
|
||||
string( REPLACE ">" "" source "${source}" )
|
||||
|
||||
# Remove keywords
|
||||
string( REGEX REPLACE "^[A-Z]+$" "" source "${source}" )
|
||||
|
||||
# Add to cleaned
|
||||
list( APPEND cleaned "${source}" )
|
||||
endforeach()
|
||||
|
||||
# Define the source groups
|
||||
if( "${prefix}" STREQUAL "" )
|
||||
source_group( TREE "${root}" FILES ${cleaned} )
|
||||
else()
|
||||
source_group( TREE "${root}" PREFIX ${prefix} FILES ${cleaned} )
|
||||
endif()
|
||||
endmacro()
|
||||
|
||||
# Given a directory, recurse to all defined subdirectories and assign
|
||||
# the given folder name to all of the targets found.
|
||||
function( set_dir_folder dir folder)
|
||||
get_property( subdirs DIRECTORY "${dir}" PROPERTY SUBDIRECTORIES )
|
||||
foreach( sub ${subdirs} )
|
||||
set_dir_folder( "${sub}" "${folder}" )
|
||||
endforeach()
|
||||
|
||||
get_property( targets DIRECTORY "${dir}" PROPERTY BUILDSYSTEM_TARGETS )
|
||||
foreach( target ${targets} )
|
||||
get_target_property( type "${target}" TYPE )
|
||||
if( NOT "${type}" STREQUAL "INTERFACE_LIBRARY" )
|
||||
set_target_properties( ${target} PROPERTIES FOLDER ${folder} )
|
||||
endif()
|
||||
endforeach()
|
||||
endfunction()
|
||||
|
||||
# Helper to retrieve the settings returned from pkg_check_modules()
|
||||
macro( get_package_interface package )
|
||||
set( INCLUDES
|
||||
${${package}_INCLUDE_DIRS}
|
||||
)
|
||||
|
||||
set( LINKDIRS
|
||||
${${package}_LIBDIR}
|
||||
)
|
||||
|
||||
# We resolve the full path of each library to ensure the
|
||||
# correct one is referenced while linking
|
||||
foreach( lib ${${package}_LIBRARIES} )
|
||||
find_library( LIB_${lib} ${lib} HINTS ${LINKDIRS} )
|
||||
list( APPEND LIBRARIES ${LIB_${lib}} )
|
||||
endforeach()
|
||||
endmacro()
|
||||
|
||||
# Set the cache and context value
|
||||
macro( set_cache_value var value )
|
||||
set( ${var} "${value}" )
|
||||
set_property( CACHE ${var} PROPERTY VALUE "${value}" )
|
||||
endmacro()
|
||||
|
||||
# Set the given property and its config specific brethren to the same value
|
||||
function( set_target_property_all target property value )
|
||||
set_target_properties( "${target}" PROPERTIES "${property}" "${value}" )
|
||||
foreach( type ${CMAKE_CONFIGURATION_TYPES} )
|
||||
string( TOUPPER "${property}_${type}" prop )
|
||||
set_target_properties( "${target}" PROPERTIES "${prop}" "${value}" )
|
||||
endforeach()
|
||||
endfunction()
|
||||
|
||||
# Taken from wxWidgets and modified for Audcaity
|
||||
#
|
||||
# cmd_option(<name> <desc> [default] [STRINGS strings])
|
||||
# The default is ON if third parameter isn't specified
|
||||
function( cmd_option name desc )
|
||||
cmake_parse_arguments( OPTION "" "" "STRINGS" ${ARGN} )
|
||||
|
||||
if( ARGC EQUAL 2 )
|
||||
if( OPTION_STRINGS )
|
||||
list( GET OPTION_STRINGS 1 default )
|
||||
else()
|
||||
set( default ON )
|
||||
endif()
|
||||
else()
|
||||
set( default ${OPTION_UNPARSED_ARGUMENTS} )
|
||||
endif()
|
||||
|
||||
if( OPTION_STRINGS )
|
||||
set( cache_type STRING )
|
||||
else()
|
||||
set( cache_type BOOL )
|
||||
endif()
|
||||
|
||||
set( ${name} "${default}" CACHE ${cache_type} "${desc}" )
|
||||
if( OPTION_STRINGS )
|
||||
set_property( CACHE ${name} PROPERTY STRINGS ${OPTION_STRINGS} )
|
||||
|
||||
# Check valid value
|
||||
set( value_is_valid FALSE )
|
||||
set( avail_values )
|
||||
foreach( opt ${OPTION_STRINGS} )
|
||||
if( ${name} STREQUAL opt )
|
||||
set( value_is_valid TRUE )
|
||||
break()
|
||||
endif()
|
||||
string( APPEND avail_values " ${opt}" )
|
||||
endforeach()
|
||||
if( NOT value_is_valid )
|
||||
message( FATAL_ERROR "Invalid value \"${${name}}\" for option ${name}. Valid values are: ${avail_values}" )
|
||||
endif()
|
||||
endif()
|
||||
|
||||
set( ${name} "${${name}}" PARENT_SCOPE )
|
||||
endfunction()
|
||||
|
||||
# Downloads NuGet packages
|
||||
#
|
||||
# Why this is needed...
|
||||
#
|
||||
# To get NuGet to work, you have to add the VS_PACKAGE_REFERENCES
|
||||
# property to a target. This target must NOT be a UTILITY target,
|
||||
# which is what we use to compile the message catalogs and assemble
|
||||
# the manual. We could add that property to the Audacity target and
|
||||
# CMake would add the required nodes to the VS project. And when the
|
||||
# Audacity target is built, the NuGet packages would get automatically
|
||||
# downloaded. This also means that the locale and manual targets
|
||||
# must be dependent on the Audacity target so the packages would get
|
||||
# downloaded before they execute. This would be handled by the CMake
|
||||
# provided ALL_BUILD target which is, by default, set as the startup
|
||||
# project in Visual Studio. Sweet right? Well, not quite...
|
||||
#
|
||||
# We want the Audacity target to be the startup project to provide
|
||||
# eaiser debugging. But, if we do that, the ALL_BUILD target is no
|
||||
# longer "in control" and any dependents of the Audacity target would
|
||||
# not get built. So, targets like "nyquist" and "plug-ins" would have
|
||||
# to be manually built. This is not what we want since Nyquist would
|
||||
# not be available during Audacity debugging because the Nyquist runtime
|
||||
# would not be copied into the destination folder alonside the Audacity
|
||||
# executable.
|
||||
#
|
||||
# To remedy this conundrum, we simply download the NuGet packages
|
||||
# ourselves and make the Audacity target dependent on the targets
|
||||
# mentioned above. This ensures that the dest folder is populated
|
||||
# and laid out like Audacity expects.
|
||||
#
|
||||
function( nuget_package dir name version )
|
||||
# Generate the full package directory name
|
||||
set( pkgdir "${CMAKE_BINARY_DIR}/packages/${name}/${version}" )
|
||||
|
||||
# Don't download it again if the package directory already exists
|
||||
if( NOT EXISTS "${pkgdir}" )
|
||||
set( pkgurl "https://www.nuget.org/api/v2/package/${name}/${version}" )
|
||||
|
||||
# Create the package directory
|
||||
file( MAKE_DIRECTORY "${pkgdir}" )
|
||||
|
||||
# And download the package into the package directory
|
||||
file( DOWNLOAD "${pkgurl}" "${pkgdir}/package.zip" )
|
||||
|
||||
# Extract the contents of the package into the package directory
|
||||
execute_process(
|
||||
COMMAND
|
||||
${CMAKE_COMMAND} -E tar x "${pkgdir}/package.zip"
|
||||
WORKING_DIRECTORY
|
||||
${pkgdir}
|
||||
)
|
||||
endif()
|
||||
|
||||
# Return the package directory name to the caller
|
||||
set( ${dir} "${pkgdir}" PARENT_SCOPE )
|
||||
endfunction()
|
@ -6,6 +6,11 @@ message( STATUS "========== Configuring ${TARGET} ==========" )
|
||||
|
||||
def_vars()
|
||||
|
||||
if( NOT DEFINED PYTHON )
|
||||
message( WARNING "Python not found...unable to produce manual." )
|
||||
return()
|
||||
endif()
|
||||
|
||||
set( host "alphamanual.audacityteam.org" )
|
||||
set( src "https://${host}/man" )
|
||||
set( dst "${_DEST}/help/manual" )
|
||||
@ -14,26 +19,11 @@ set( script "mw2html.py" )
|
||||
set( out_dir "${_INTDIR}" )
|
||||
set( out "${out_dir}/${host}/index.html" )
|
||||
|
||||
if( CMAKE_SYSTEM_NAME MATCHES "Windows" )
|
||||
nuget_package( pkgdir "python2" "2.7.17" )
|
||||
file( TO_NATIVE_PATH "${pkgdir}/tools/python.exe" python )
|
||||
else()
|
||||
find_package( Python2 )
|
||||
if( Python2_FOUND )
|
||||
set( python "${Python2_EXECUTABLE}" )
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if( NOT DEFINED python )
|
||||
message( WARNING "Python not found...unable to produce manual." )
|
||||
return()
|
||||
endif()
|
||||
|
||||
add_custom_command(
|
||||
COMMENT
|
||||
"Downloading manual from: ${src}"
|
||||
COMMAND
|
||||
"${python}" "${script_dir}/${script}" -s "${src}" "${out_dir}"
|
||||
"${PYTHON}" "${script_dir}/${script}" -s "${src}" "${out_dir}"
|
||||
COMMAND
|
||||
${CMAKE_COMMAND} -E copy_directory "${out_dir}/${host}" "${dst}"
|
||||
WORKING_DIRECTORY
|
||||
|
@ -64,69 +64,21 @@ list( APPEND SOURCES
|
||||
zh_TW.po
|
||||
)
|
||||
|
||||
if( CMAKE_SYSTEM_NAME MATCHES "Windows" )
|
||||
nuget_package( pkgdir "Gettext.Tools" "0.20.1.1" )
|
||||
file( TO_NATIVE_PATH "${pkgdir}/tools/bin/msgfmt.exe" msgfmt )
|
||||
elseif( CMAKE_SYSTEM_NAME MATCHES "Darwin" )
|
||||
find_package( Gettext )
|
||||
# Look for gettext
|
||||
find_package( Gettext QUIET )
|
||||
if( GETTEXT_FOUND )
|
||||
mark_as_advanced( FORCE GETTEXT_MSGFMT_EXECUTABLE )
|
||||
mark_as_advanced( FORCE GETTEXT_MSGMERGE_EXECUTABLE )
|
||||
|
||||
if( GETTEXT_FOUND )
|
||||
set( msgfmt "${GETTEXT_MSGFMT_EXECUTABLE}" )
|
||||
else()
|
||||
set( root "${CMAKE_CURRENT_BINARY_DIR}/usr/local" )
|
||||
set( msgfmt "${CMAKE_CURRENT_BINARY_DIR}/msgfmt" )
|
||||
|
||||
if( NOT EXISTS "${root}" )
|
||||
execute_process(
|
||||
COMMAND
|
||||
curl -L -o /tmp/gettext.pkg https://raw.githubusercontent.com/rudix-mac/packages/master/gettext-0.20.1-macos10.14.pkg
|
||||
)
|
||||
execute_process(
|
||||
COMMAND
|
||||
pkgutil --force --expand /tmp/gettext.pkg /tmp/gettext
|
||||
)
|
||||
execute_process(
|
||||
COMMAND
|
||||
tar -C "${CMAKE_CURRENT_BINARY_DIR}" -x -f /tmp/gettext/gettextinstall.pkg/Payload
|
||||
)
|
||||
execute_process(
|
||||
COMMAND
|
||||
rm -rf /tmp/gettext.pkg /tmp/gettext
|
||||
)
|
||||
endif()
|
||||
|
||||
file( WRITE ${CMAKE_BINARY_DIR}/msgfmt
|
||||
"#!/bin/sh\n"
|
||||
"export DYLD_LIBRARY_PATH=${root}/lib\n"
|
||||
"${root}/bin/msgfmt \$@ \n"
|
||||
)
|
||||
|
||||
file( COPY ${CMAKE_BINARY_DIR}/msgfmt
|
||||
DESTINATION ${CMAKE_CURRENT_BINARY_DIR}
|
||||
FILE_PERMISSIONS
|
||||
OWNER_READ OWNER_EXECUTE
|
||||
GROUP_READ GROUP_EXECUTE
|
||||
WORLD_READ WORLD_EXECUTE
|
||||
)
|
||||
|
||||
file( REMOVE ${CMAKE_BINARY_DIR}/msgfmt )
|
||||
|
||||
endif()
|
||||
|
||||
|
||||
else()
|
||||
|
||||
find_package( Gettext )
|
||||
if( GETTEXT_FOUND )
|
||||
set( msgfmt "${GETTEXT_MSGFMT_EXECUTABLE}" )
|
||||
endif()
|
||||
|
||||
set( msgfmt "${GETTEXT_MSGFMT_EXECUTABLE}" )
|
||||
elseif( PYTHON )
|
||||
set( msgfmt "${PYTHON}" "${TARGET_ROOT}/msgfmt.py" )
|
||||
elseif( CMAKE_SYSTEM_NAME MATCHES "Windows" )
|
||||
nuget_package( pkgdir "Gettext.Tools" "0.20.1.1" )
|
||||
file( TO_NATIVE_PATH "${pkgdir}/tools/bin/msgfmt.exe" msgfmt )
|
||||
endif()
|
||||
|
||||
if( NOT DEFINED msgfmt )
|
||||
message( WARNING "Gettext not found...translations will not be provided." )
|
||||
message( WARNING "The msgfmt program wasn't found...translations will not be provided." )
|
||||
return()
|
||||
endif()
|
||||
|
||||
@ -159,7 +111,7 @@ foreach( source ${SOURCES} )
|
||||
COMMAND
|
||||
"${CMAKE_COMMAND}" -E make_directory "${dst}"
|
||||
COMMAND
|
||||
"${msgfmt}" -o "${mo}" "${po}"
|
||||
${msgfmt} -o "${mo}" "${po}"
|
||||
OUTPUT
|
||||
"${mo}"
|
||||
)
|
||||
|
305
locale/msgfmt.py
Normal file
305
locale/msgfmt.py
Normal file
@ -0,0 +1,305 @@
|
||||
#! /usr/bin/env python
|
||||
# -*- coding: iso-8859-1 -*-
|
||||
# Written by Martin v. Loewis <loewis@informatik.hu-berlin.de>
|
||||
#
|
||||
# Changed by Christian 'Tiran' Heimes <tiran@cheimes.de> for the placeless
|
||||
# translation service (PTS) of Zope
|
||||
#
|
||||
# Fixed some bugs and updated to support msgctxt
|
||||
# by Hanno Schlichting <hanno@hannosch.eu>
|
||||
|
||||
"""Generate binary message catalog from textual translation description.
|
||||
|
||||
This program converts a textual Uniforum-style message catalog (.po file) into
|
||||
a binary GNU catalog (.mo file). This is essentially the same function as the
|
||||
GNU msgfmt program, however, it is a simpler implementation.
|
||||
|
||||
This file was taken from Python-2.3.2/Tools/i18n and altered in several ways.
|
||||
Now you can simply use it from another python module:
|
||||
|
||||
from msgfmt import Msgfmt
|
||||
mo = Msgfmt(po).get()
|
||||
|
||||
where po is path to a po file as string, an opened po file ready for reading or
|
||||
a list of strings (readlines of a po file) and mo is the compiled mo file as
|
||||
binary string.
|
||||
|
||||
Exceptions:
|
||||
|
||||
* IOError if the file couldn't be read
|
||||
|
||||
* msgfmt.PoSyntaxError if the po file has syntax errors
|
||||
"""
|
||||
|
||||
from __future__ import print_function
|
||||
import array
|
||||
from ast import literal_eval
|
||||
import codecs
|
||||
from email.parser import HeaderParser
|
||||
import getopt
|
||||
import struct
|
||||
import sys
|
||||
|
||||
PY3 = sys.version_info[0] == 3
|
||||
if PY3:
|
||||
def header_charset(s):
|
||||
p = HeaderParser()
|
||||
return p.parsestr(s).get_content_charset()
|
||||
|
||||
import io
|
||||
BytesIO = io.BytesIO
|
||||
FILE_TYPE = io.IOBase
|
||||
else:
|
||||
def header_charset(s):
|
||||
p = HeaderParser()
|
||||
return p.parsestr(s.encode('utf-8', 'ignore')).get_content_charset()
|
||||
|
||||
from cStringIO import StringIO as BytesIO
|
||||
FILE_TYPE = file
|
||||
|
||||
|
||||
class PoSyntaxError(Exception):
|
||||
""" Syntax error in a po file """
|
||||
|
||||
def __init__(self, msg):
|
||||
self.msg = msg
|
||||
|
||||
def __str__(self):
|
||||
return 'Po file syntax error: %s' % self.msg
|
||||
|
||||
|
||||
class Msgfmt:
|
||||
|
||||
def __init__(self, po, name='unknown'):
|
||||
self.po = po
|
||||
self.name = name
|
||||
self.messages = {}
|
||||
self.openfile = False
|
||||
# Start off assuming latin-1, so everything decodes without failure,
|
||||
# until we know the exact encoding
|
||||
self.encoding = 'latin-1'
|
||||
|
||||
def readPoData(self):
|
||||
""" read po data from self.po and return an iterator """
|
||||
output = []
|
||||
if isinstance(self.po, str):
|
||||
output = open(self.po, 'rb')
|
||||
elif isinstance(self.po, FILE_TYPE):
|
||||
self.po.seek(0)
|
||||
self.openfile = True
|
||||
output = self.po
|
||||
elif isinstance(self.po, list):
|
||||
output = self.po
|
||||
if not output:
|
||||
raise ValueError("self.po is invalid! %s" % type(self.po))
|
||||
if isinstance(output, FILE_TYPE):
|
||||
# remove BOM from the start of the parsed input
|
||||
first = output.readline()
|
||||
if len(first) == 0:
|
||||
return output.readlines()
|
||||
if first.startswith(codecs.BOM_UTF8):
|
||||
first = first.lstrip(codecs.BOM_UTF8)
|
||||
return [first] + output.readlines()
|
||||
return output
|
||||
|
||||
def add(self, context, id, string, fuzzy):
|
||||
"Add a non-empty and non-fuzzy translation to the dictionary."
|
||||
if string and not fuzzy:
|
||||
# The context is put before the id and separated by a EOT char.
|
||||
if context:
|
||||
id = context + u'\x04' + id
|
||||
if not id:
|
||||
# See whether there is an encoding declaration
|
||||
charset = header_charset(string)
|
||||
if charset:
|
||||
# decode header in proper encoding
|
||||
string = string.encode(self.encoding).decode(charset)
|
||||
if not PY3:
|
||||
# undo damage done by literal_eval in Python 2.x
|
||||
string = string.encode(self.encoding).decode(charset)
|
||||
self.encoding = charset
|
||||
self.messages[id] = string
|
||||
|
||||
def generate(self):
|
||||
"Return the generated output."
|
||||
# the keys are sorted in the .mo file
|
||||
keys = sorted(self.messages.keys())
|
||||
offsets = []
|
||||
ids = strs = b''
|
||||
for id in keys:
|
||||
msg = self.messages[id].encode(self.encoding)
|
||||
id = id.encode(self.encoding)
|
||||
# For each string, we need size and file offset. Each string is
|
||||
# NUL terminated; the NUL does not count into the size.
|
||||
offsets.append((len(ids), len(id), len(strs),
|
||||
len(msg)))
|
||||
ids += id + b'\0'
|
||||
strs += msg + b'\0'
|
||||
output = b''
|
||||
# The header is 7 32-bit unsigned integers. We don't use hash tables,
|
||||
# so the keys start right after the index tables.
|
||||
keystart = 7 * 4 + 16 * len(keys)
|
||||
# and the values start after the keys
|
||||
valuestart = keystart + len(ids)
|
||||
koffsets = []
|
||||
voffsets = []
|
||||
# The string table first has the list of keys, then the list of values.
|
||||
# Each entry has first the size of the string, then the file offset.
|
||||
for o1, l1, o2, l2 in offsets:
|
||||
koffsets += [l1, o1 + keystart]
|
||||
voffsets += [l2, o2 + valuestart]
|
||||
offsets = koffsets + voffsets
|
||||
# Even though we don't use a hashtable, we still set its offset to be
|
||||
# binary compatible with the gnu gettext format produced by:
|
||||
# msgfmt file.po --no-hash
|
||||
output = struct.pack("Iiiiiii",
|
||||
0x950412de, # Magic
|
||||
0, # Version
|
||||
len(keys), # # of entries
|
||||
7 * 4, # start of key index
|
||||
7 * 4 + len(keys) * 8, # start of value index
|
||||
0, keystart) # size and offset of hash table
|
||||
if PY3:
|
||||
output += array.array("i", offsets).tobytes()
|
||||
else:
|
||||
output += array.array("i", offsets).tostring()
|
||||
output += ids
|
||||
output += strs
|
||||
return output
|
||||
|
||||
def get(self):
|
||||
""" """
|
||||
self.read()
|
||||
# Compute output
|
||||
return self.generate()
|
||||
|
||||
def read(self, header_only=False):
|
||||
""" """
|
||||
ID = 1
|
||||
STR = 2
|
||||
CTXT = 3
|
||||
|
||||
section = None
|
||||
fuzzy = 0
|
||||
msgid = msgstr = msgctxt = u''
|
||||
|
||||
# Parse the catalog
|
||||
lno = 0
|
||||
for l in self.readPoData():
|
||||
l = l.decode(self.encoding)
|
||||
lno += 1
|
||||
# If we get a comment line after a msgstr or a line starting with
|
||||
# msgid or msgctxt, this is a new entry
|
||||
if section == STR and (l[0] == '#' or (l[0] == 'm' and
|
||||
(l.startswith('msgctxt') or l.startswith('msgid')))):
|
||||
self.add(msgctxt, msgid, msgstr, fuzzy)
|
||||
section = None
|
||||
fuzzy = 0
|
||||
# If we only want the header we stop after the first message
|
||||
if header_only:
|
||||
break
|
||||
# Record a fuzzy mark
|
||||
if l[:2] == '#,' and 'fuzzy' in l:
|
||||
fuzzy = 1
|
||||
# Skip comments
|
||||
if l[0] == '#':
|
||||
continue
|
||||
# Now we are in a msgctxt section
|
||||
if l.startswith('msgctxt'):
|
||||
section = CTXT
|
||||
l = l[7:]
|
||||
msgctxt = u''
|
||||
# Now we are in a msgid section, output previous section
|
||||
elif (l.startswith('msgid') and
|
||||
not l.startswith('msgid_plural')):
|
||||
if section == STR:
|
||||
self.add(msgid, msgstr, fuzzy)
|
||||
section = ID
|
||||
l = l[5:]
|
||||
msgid = msgstr = u''
|
||||
is_plural = False
|
||||
# This is a message with plural forms
|
||||
elif l.startswith('msgid_plural'):
|
||||
if section != ID:
|
||||
raise PoSyntaxError(
|
||||
'msgid_plural not preceeded by '
|
||||
'msgid on line %d of po file %s' %
|
||||
(lno, repr(self.name)))
|
||||
l = l[12:]
|
||||
msgid += u'\0' # separator of singular and plural
|
||||
is_plural = True
|
||||
# Now we are in a msgstr section
|
||||
elif l.startswith('msgstr'):
|
||||
section = STR
|
||||
if l.startswith('msgstr['):
|
||||
if not is_plural:
|
||||
raise PoSyntaxError(
|
||||
'plural without msgid_plural '
|
||||
'on line %d of po file %s' %
|
||||
(lno, repr(self.name)))
|
||||
l = l.split(']', 1)[1]
|
||||
if msgstr:
|
||||
# Separator of the various plural forms
|
||||
msgstr += u'\0'
|
||||
else:
|
||||
if is_plural:
|
||||
raise PoSyntaxError(
|
||||
'indexed msgstr required for '
|
||||
'plural on line %d of po file %s' %
|
||||
(lno, repr(self.name)))
|
||||
l = l[6:]
|
||||
# Skip empty lines
|
||||
l = l.strip()
|
||||
if not l:
|
||||
continue
|
||||
# TODO: Does this always follow Python escape semantics?
|
||||
try:
|
||||
l = literal_eval(l)
|
||||
except Exception as msg:
|
||||
raise PoSyntaxError(
|
||||
'%s (line %d of po file %s): \n%s' %
|
||||
(msg, lno, repr(self.name), l))
|
||||
if isinstance(l, bytes):
|
||||
l = l.decode(self.encoding)
|
||||
if section == CTXT:
|
||||
msgctxt += l
|
||||
elif section == ID:
|
||||
msgid += l
|
||||
elif section == STR:
|
||||
msgstr += l
|
||||
else:
|
||||
raise PoSyntaxError(
|
||||
'error on line %d of po file %s' %
|
||||
(lno, repr(self.name)))
|
||||
|
||||
# Add last entry
|
||||
if section == STR:
|
||||
self.add(msgctxt, msgid, msgstr, fuzzy)
|
||||
|
||||
if self.openfile:
|
||||
self.po.close()
|
||||
|
||||
def getAsFile(self):
|
||||
return BytesIO(self.get())
|
||||
|
||||
def main():
|
||||
try:
|
||||
opts, args = getopt.getopt(sys.argv[1:], 'o:')
|
||||
except getopt.error as msg:
|
||||
print(msg, file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
if not args:
|
||||
print('No input file given', file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
if not opts:
|
||||
print('No output file given', file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
with open(opts[0][1], "w") as mo:
|
||||
mo.write(Msgfmt(args[0]).get())
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
@ -1015,6 +1015,10 @@ list( APPEND DEFINES
|
||||
WXINTL_NO_GETTEXT_MACRO
|
||||
WXUSINGDLL
|
||||
CMAKE
|
||||
$<$<BOOL:${GIT_FOUND}>:
|
||||
REV_LONG="${GIT_COMMIT_LONG}"
|
||||
REV_TIME="${GIT_COMMIT_TIME}"
|
||||
>
|
||||
$<$<BOOL:${HAVE_LRINT}>:
|
||||
HAVE_LRINT
|
||||
>
|
||||
|
Loading…
x
Reference in New Issue
Block a user