1#
2# A collection of functions and macros
3#
4
5# Defines several useful directory paths for the active context.
6macro( def_vars )
7   set( _SRCDIR "${CMAKE_CURRENT_SOURCE_DIR}" )
8   set( _INTDIR "${CMAKE_CURRENT_BINARY_DIR}" )
9   set( _PRVDIR "${CMAKE_CURRENT_BINARY_DIR}/private" )
10   set( _PUBDIR "${CMAKE_CURRENT_BINARY_DIR}/public" )
11endmacro()
12
13# Helper to organize sources into folders for the IDEs
14macro( organize_source root prefix sources )
15   set( cleaned )
16   foreach( source ${sources} )
17      # Remove generator expressions
18      string( REGEX REPLACE ".*>:(.*)>*" "\\1" source "${source}" )
19      string( REPLACE ">" "" source "${source}" )
20
21      # Remove keywords
22      string( REGEX REPLACE "^[A-Z]+$" "" source "${source}" )
23
24      # Add to cleaned
25      list( APPEND cleaned "${source}" )
26   endforeach()
27
28   # Define the source groups
29   if( "${prefix}" STREQUAL "" )
30      source_group( TREE "${root}" FILES ${cleaned} )
31   else()
32      source_group( TREE "${root}" PREFIX ${prefix} FILES ${cleaned} )
33   endif()
34endmacro()
35
36# Given a directory, recurse to all defined subdirectories and assign
37# the given folder name to all of the targets found.
38function( set_dir_folder dir folder)
39   get_property( subdirs DIRECTORY "${dir}" PROPERTY SUBDIRECTORIES )
40   foreach( sub ${subdirs} )
41      set_dir_folder( "${sub}" "${folder}" )
42   endforeach()
43
44   get_property( targets DIRECTORY "${dir}" PROPERTY BUILDSYSTEM_TARGETS )
45   foreach( target ${targets} )
46      get_target_property( type "${target}" TYPE )
47      if( NOT "${type}" STREQUAL "INTERFACE_LIBRARY" )
48         set_target_properties( ${target} PROPERTIES FOLDER ${folder} )
49      endif()
50   endforeach()
51endfunction()
52
53# Helper to retrieve the settings returned from pkg_check_modules()
54function( get_package_interface package target )
55   set( package_includes
56      ${${package}_INCLUDE_DIRS}
57   )
58
59   set( package_linkdirs
60      ${${package}_LIBDIR}
61   )
62
63   # We resolve the full path of each library to ensure the
64   # correct one is referenced while linking
65   set( package_libraries )
66   foreach( lib ${${package}_LIBRARIES} )
67      find_library( LIB_${lib} ${lib} HINTS ${package_linkdirs} )
68      list( APPEND package_libraries ${LIB_${lib}} )
69   endforeach()
70
71   # And add it to our target
72   target_include_directories( ${target} INTERFACE ${package_includes} )
73   target_link_libraries( ${target} INTERFACE ${package_libraries} )
74
75   message(STATUS "Interface ${target}:\n\tinclude: ${includes}\n\tLibraries: ${LIBRARIES}")
76endfunction()
77
78# Set the cache and context value
79macro( set_cache_value var value )
80   set( ${var} "${value}" )
81   set_property( CACHE ${var} PROPERTY VALUE "${value}" )
82endmacro()
83
84# Set a CMake variable to the value of the corresponding environment variable
85# if the CMake variable is not already defined. Any addition arguments after
86# the variable name are passed through to set().
87macro( set_from_env var )
88   if( NOT DEFINED ${var} AND NOT "$ENV{${var}}" STREQUAL "" )
89      set( ${var} "$ENV{${var}}" ${ARGN} ) # pass additional args (e.g. CACHE)
90   endif()
91endmacro()
92
93# Set the given property and its config specific brethren to the same value
94function( set_target_property_all target property value )
95   set_target_properties( "${target}" PROPERTIES "${property}" "${value}" )
96   foreach( type ${CMAKE_CONFIGURATION_TYPES} )
97      string( TOUPPER "${property}_${type}" prop )
98      set_target_properties( "${target}" PROPERTIES "${prop}" "${value}" )
99   endforeach()
100endfunction()
101
102# Taken from wxWidgets and modified for Audacity
103#
104# cmd_option(<name> <desc> [default] [STRINGS strings])
105# The default is ON if third parameter isn't specified
106function( cmd_option name desc )
107   cmake_parse_arguments( OPTION "" "" "STRINGS" ${ARGN} )
108
109   if( ARGC EQUAL 2 )
110      if( OPTION_STRINGS )
111         list( GET OPTION_STRINGS 1 default )
112      else()
113         set( default ON )
114      endif()
115   else()
116      set( default ${OPTION_UNPARSED_ARGUMENTS} )
117   endif()
118
119   if( OPTION_STRINGS )
120      set( cache_type STRING )
121   else()
122      set( cache_type BOOL )
123   endif()
124
125   set( ${name} "${default}" CACHE ${cache_type} "${desc}" )
126   if( OPTION_STRINGS )
127      set_property( CACHE ${name} PROPERTY STRINGS ${OPTION_STRINGS} )
128
129      # Check valid value
130      set( value_is_valid FALSE )
131      set( avail_values )
132      foreach( opt ${OPTION_STRINGS} )
133         if( ${name} STREQUAL opt )
134            set( value_is_valid TRUE )
135            break()
136         endif()
137         string( APPEND avail_values " ${opt}" )
138      endforeach()
139      if( NOT value_is_valid )
140         message( FATAL_ERROR "Invalid value \"${${name}}\" for option ${name}. Valid values are: ${avail_values}" )
141      endif()
142   endif()
143
144   set( ${name} "${${name}}" PARENT_SCOPE )
145endfunction()
146
147# Downloads NuGet packages
148#
149# Why this is needed...
150#
151# To get NuGet to work, you have to add the VS_PACKAGE_REFERENCES
152# property to a target. This target must NOT be a UTILITY target,
153# which is what we use to compile the message catalogs and assemble
154# the manual. We could add that property to the Audacity target and
155# CMake would add the required nodes to the VS project. And when the
156# Audacity target is built, the NuGet packages would get automatically
157# downloaded. This also means that the locale and manual targets
158# must be dependent on the Audacity target so the packages would get
159# downloaded before they execute. This would be handled by the CMake
160# provided ALL_BUILD target which is, by default, set as the startup
161# project in Visual Studio. Sweet right? Well, not quite...
162#
163# We want the Audacity target to be the startup project to provide
164# easier debugging. But, if we do that, the ALL_BUILD target is no
165# longer "in control" and any dependents of the Audacity target would
166# not get built. So, targets like "nyquist" and "plug-ins" would have
167# to be manually built. This is not what we want since Nyquist would
168# not be available during Audacity debugging because the Nyquist runtime
169# would not be copied into the destination folder alonside the Audacity
170# executable.
171#
172# To remedy this conundrum, we simply download the NuGet packages
173# ourselves and make the Audacity target dependent on the targets
174# mentioned above. This ensures that the dest folder is populated
175# and laid out like Audacity expects.
176#
177function( nuget_package dir name version )
178   # Generate the full package directory name
179   set( pkgdir "${CMAKE_BINARY_DIR}/packages/${name}/${version}" )
180
181   # Don't download it again if the package directory already exists
182   if( NOT EXISTS "${pkgdir}" )
183      set( pkgurl "https://www.nuget.org/api/v2/package/${name}/${version}" )
184
185      # Create the package directory
186      file( MAKE_DIRECTORY "${pkgdir}" )
187
188      # And download the package into the package directory
189      file( DOWNLOAD "${pkgurl}" "${pkgdir}/package.zip" )
190
191      # Extract the contents of the package into the package directory
192      execute_process(
193         COMMAND
194            ${CMAKE_COMMAND} -E tar x "${pkgdir}/package.zip"
195         WORKING_DIRECTORY
196            ${pkgdir}
197      )
198   endif()
199
200   # Return the package directory name to the caller
201   set( ${dir} "${pkgdir}" PARENT_SCOPE )
202endfunction()
203
204# Determines if the linker supports the "-platform_version" argument
205# on macOS.
206macro( check_for_platform_version )
207   if( NOT DEFINED LINKER_SUPPORTS_PLATFORM_VERSION )
208      execute_process(
209         COMMAND
210            ld -platform_version macos 1.1 1.1
211         ERROR_VARIABLE
212            error
213      )
214
215      if( error MATCHES ".*unknown option.*" )
216         set( PLATFORM_VERSION_SUPPORTED no CACHE INTERNAL "" )
217      else()
218         set( PLATFORM_VERSION_SUPPORTED yes CACHE INTERNAL "" )
219      endif()
220   endif()
221endmacro()
222
223# To be used to compile all C++ in the application and modules
224function( audacity_append_common_compiler_options var use_pch )
225   if( NOT use_pch )
226      list( APPEND ${var}
227         PRIVATE
228            # include the correct config file; give absolute path to it, so
229            # that this works whether in src, modules, libraries
230            $<$<PLATFORM_ID:Windows>:/FI${CMAKE_BINARY_DIR}/src/private/configwin.h>
231            $<$<PLATFORM_ID:Darwin>:-include ${CMAKE_BINARY_DIR}/src/private/configmac.h>
232            $<$<NOT:$<PLATFORM_ID:Windows,Darwin>>:-include ${CMAKE_BINARY_DIR}/src/private/configunix.h>
233      )
234   endif()
235   list( APPEND ${var}
236         -DAUDACITY_VERSION=${AUDACITY_VERSION}
237         -DAUDACITY_RELEASE=${AUDACITY_RELEASE}
238         -DAUDACITY_REVISION=${AUDACITY_REVISION}
239         -DAUDACITY_MODLEVEL=${AUDACITY_MODLEVEL}
240
241         # Version string for visual display
242         -DAUDACITY_VERSION_STRING=L"${AUDACITY_VERSION}.${AUDACITY_RELEASE}.${AUDACITY_REVISION}${AUDACITY_SUFFIX}"
243
244         # This value is used in the resource compiler for Windows
245         -DAUDACITY_FILE_VERSION=L"${AUDACITY_VERSION},${AUDACITY_RELEASE},${AUDACITY_REVISION},${AUDACITY_MODLEVEL}"
246
247         # This renames a good use of this C++ keyword that we don't need
248	      # to review when hunting for leaks because of naked new and delete.
249	 -DPROHIBITED==delete
250
251         # Reviewed, certified, non-leaky uses of NEW that immediately entrust
252	      # their results to RAII objects.
253         # You may use it in NEW code when constructing a wxWindow subclass
254	      # with non-NULL parent window.
255         # You may use it in NEW code when the NEW expression is the
256	      # constructor argument for a standard smart
257         # pointer like std::unique_ptr or std::shared_ptr.
258         -Dsafenew=new
259
260         $<$<CXX_COMPILER_ID:MSVC>:/permissive->
261         $<$<CXX_COMPILER_ID:AppleClang,Clang>:-Wno-underaligned-exception-object>
262         $<$<CXX_COMPILER_ID:AppleClang,Clang>:-Werror=return-type>
263         $<$<CXX_COMPILER_ID:AppleClang,Clang>:-Werror=dangling-else>
264         $<$<CXX_COMPILER_ID:AppleClang,Clang>:-Werror=return-stack-address>
265	      # Yes, CMake will change -D to /D as needed for Windows:
266         -DWXINTL_NO_GETTEXT_MACRO
267         $<$<CXX_COMPILER_ID:MSVC>:-D_USE_MATH_DEFINES>
268         $<$<CXX_COMPILER_ID:MSVC>:-DNOMINMAX>
269
270         # Define/undefine _DEBUG
271	 # Yes, -U to /U too as needed for Windows:
272	 $<IF:$<CONFIG:Debug>,-D_DEBUG=1,-U_DEBUG>
273
274         $<$<PLATFORM_ID:Darwin>:-DUSE_AQUA_THEME>
275   )
276   # Definitions controlled by the AUDACITY_BUILD_LEVEL switch
277   if( AUDACITY_BUILD_LEVEL EQUAL 0 )
278      list( APPEND ${var} -DIS_ALPHA -DUSE_ALPHA_MANUAL )
279   elseif( AUDACITY_BUILD_LEVEL EQUAL 1 )
280      list( APPEND ${var} -DIS_BETA -DUSE_ALPHA_MANUAL )
281   else()
282      list( APPEND ${var} -DIS_RELEASE )
283   endif()
284
285   set( ${var} "${${var}}" PARENT_SCOPE )
286endfunction()
287
288function( import_export_symbol var module_name )
289   # compute, e.g. "TRACK_UI_API" from module name "mod-track-ui"
290   string( REGEX REPLACE "^mod-" "" symbol "${module_name}" )
291   string( REGEX REPLACE "^lib-" "" symbol "${symbol}" )
292   string( TOUPPER "${symbol}" symbol )
293   string( REPLACE "-" "_" symbol "${symbol}" )
294   string( APPEND symbol "_API" )
295   set( "${var}" "${symbol}" PARENT_SCOPE )
296endfunction()
297
298function( import_symbol_define var module_name )
299   import_export_symbol( symbol "${module_name}" )
300   if( CMAKE_SYSTEM_NAME MATCHES "Windows" )
301      set( value "_declspec(dllimport)" )
302   elseif( HAVE_VISIBILITY )
303      set( value "__attribute__((visibility(\"default\")))" )
304   else()
305      set( value "" )
306   endif()
307   set( "${var}" "${symbol}=${value}" PARENT_SCOPE )
308endfunction()
309
310function( export_symbol_define var module_name )
311   import_export_symbol( symbol "${module_name}" )
312   if( CMAKE_SYSTEM_NAME MATCHES "Windows" )
313      set( value "_declspec(dllexport)" )
314   elseif( HAVE_VISIBILITY )
315      set( value "__attribute__((visibility(\"default\")))" )
316   else()
317      set( value "" )
318   endif()
319   set( "${var}" "${symbol}=${value}" PARENT_SCOPE )
320endfunction()
321
322# shorten a target name for purposes of generating a dependency graph picture
323function( canonicalize_node_name var node )
324   # strip generator expressions
325   string( REGEX REPLACE ".*>.*:(.*)>" "\\1" node "${node}" )
326   # omit the "-interface" for alias targets to modules
327   string( REGEX REPLACE "-interface\$" "" node "${node}"  )
328   # shorten names of standard libraries or Apple frameworks
329   string( REGEX REPLACE "^-(l|framework )" "" node "${node}" )
330   # shorten paths
331   get_filename_component( node "${node}" NAME_WE )
332   set( "${var}" "${node}" PARENT_SCOPE )
333endfunction()
334
335function( audacity_module_fn NAME SOURCES IMPORT_TARGETS
336   ADDITIONAL_DEFINES ADDITIONAL_LIBRARIES LIBTYPE )
337
338   set( TARGET ${NAME} )
339   set( TARGET_ROOT ${CMAKE_CURRENT_SOURCE_DIR} )
340
341   message( STATUS "========== Configuring ${TARGET} ==========" )
342
343   def_vars()
344
345   if (LIBTYPE STREQUAL "MODULE" AND CMAKE_SYSTEM_NAME MATCHES "Windows")
346      set( REAL_LIBTYPE SHARED )
347   else()
348      set( REAL_LIBTYPE "${LIBTYPE}" )
349   endif()
350   add_library( ${TARGET} ${REAL_LIBTYPE} )
351
352   # Manual propagation seems to be necessary from
353   # interface libraries -- just doing target_link_libraries naming them
354   # doesn't work as promised
355
356   # compute INCLUDES
357   set( INCLUDES )
358   list( APPEND INCLUDES PUBLIC ${TARGET_ROOT} )
359
360   # compute DEFINES
361   set( DEFINES )
362   list( APPEND DEFINES ${ADDITIONAL_DEFINES} )
363
364   # send the file to the proper place in the build tree, by setting the
365   # appropriate property for the platform
366   if (CMAKE_SYSTEM_NAME MATCHES "Windows")
367      set( DIRECTORY_PROPERTY RUNTIME_OUTPUT_DIRECTORY )
368   else ()
369      set( DIRECTORY_PROPERTY LIBRARY_OUTPUT_DIRECTORY )
370   endif ()
371
372   if (LIBTYPE STREQUAL "MODULE")
373      set( ATTRIBUTES "shape=box" )
374      set_target_property_all( ${TARGET} ${DIRECTORY_PROPERTY} "${_MODDIR}" )
375      set_target_properties( ${TARGET}
376         PROPERTIES
377            PREFIX ""
378            FOLDER "modules" # for IDE organization
379      )
380      if( CMAKE_HOST_SYSTEM_NAME MATCHES "Darwin" )
381         add_custom_command(
382	    TARGET ${TARGET}
383            COMMAND ${CMAKE_COMMAND}
384	       -D SRC="${_MODDIR}/${TARGET}.so"
385               -D WXWIN="${_SHARED_PROXY_BASE_PATH}/$<CONFIG>"
386               -P ${AUDACITY_MODULE_PATH}/CopyLibs.cmake
387            POST_BUILD )
388      endif()
389   else()
390      set( ATTRIBUTES "shape=octagon" )
391      set_target_property_all( ${TARGET} ${DIRECTORY_PROPERTY} "${_SHARED_PROXY_PATH}" )
392      set_target_properties( ${TARGET}
393         PROPERTIES
394            PREFIX ""
395            FOLDER "libraries" # for IDE organization
396            INSTALL_NAME_DIR ""
397            BUILD_WITH_INSTALL_NAME_DIR YES
398      )
399   endif()
400
401   if( "wxBase" IN_LIST IMPORT_TARGETS OR "wxwidgets::base" IN_LIST IMPORT_TARGETS )
402      string( APPEND ATTRIBUTES " style=filled" )
403   endif()
404
405   export_symbol_define( export_symbol "${TARGET}" )
406   import_symbol_define( import_symbol "${TARGET}" )
407
408   list( APPEND DEFINES
409      PRIVATE "${export_symbol}"
410      INTERFACE "${import_symbol}"
411   )
412
413   set( LOPTS
414      PRIVATE
415         $<$<PLATFORM_ID:Darwin>:-undefined dynamic_lookup>
416   )
417
418   # compute LIBRARIES
419   set( LIBRARIES )
420
421   foreach( IMPORT ${IMPORT_TARGETS} )
422      list( APPEND LIBRARIES "${IMPORT}" )
423   endforeach()
424
425   list( APPEND LIBRARIES ${ADDITIONAL_LIBRARIES} )
426
427#   list( TRANSFORM SOURCES PREPEND "${CMAKE_CURRENT_SOURCE_DIR}/" )
428
429   # Compute compilation options.
430   # Perhaps a another function argument in future to customize this too.
431   set( OPTIONS )
432   audacity_append_common_compiler_options( OPTIONS NO )
433
434   organize_source( "${TARGET_ROOT}" "" "${SOURCES}" )
435   target_sources( ${TARGET} PRIVATE ${SOURCES} )
436   target_compile_definitions( ${TARGET} PRIVATE ${DEFINES} )
437   target_compile_options( ${TARGET} ${OPTIONS} )
438   target_include_directories( ${TARGET} PUBLIC ${TARGET_ROOT} )
439
440   target_link_options( ${TARGET} PRIVATE ${LOPTS} )
441   target_link_libraries( ${TARGET} PUBLIC ${LIBRARIES} )
442
443   if( NOT CMAKE_SYSTEM_NAME MATCHES "Windows" )
444      add_custom_command(
445         TARGET "${TARGET}"
446         POST_BUILD
447         COMMAND $<IF:$<CONFIG:Debug>,echo,strip> -x $<TARGET_FILE:${TARGET}>
448      )
449   endif()
450
451   if( NOT REAL_LIBTYPE STREQUAL "MODULE" )
452      if( CMAKE_SYSTEM_NAME MATCHES "Windows" )
453         set( REQUIRED_LOCATION "${_EXEDIR}" )
454      elseif( CMAKE_SYSTEM_NAME MATCHES "Darwin")
455         set( REQUIRED_LOCATION "${_PKGLIB}" )
456      else()
457         set( REQUIRED_LOCATION "${_DEST}/${_PKGLIB}" )
458      endif()
459
460      add_custom_command(TARGET ${TARGET} POST_BUILD
461         COMMAND ${CMAKE_COMMAND} -E copy
462            "$<TARGET_FILE:${TARGET}>"
463            "${REQUIRED_LOCATION}/$<TARGET_FILE_NAME:${TARGET}>"
464      )
465   endif()
466
467   # define an additional interface library target
468   set(INTERFACE_TARGET "${TARGET}-interface")
469   if (NOT REAL_LIBTYPE STREQUAL "MODULE")
470      add_library("${INTERFACE_TARGET}" ALIAS "${TARGET}")
471   else()
472      add_library("${INTERFACE_TARGET}" INTERFACE)
473      foreach(PROP
474         INTERFACE_INCLUDE_DIRECTORIES
475         INTERFACE_COMPILE_DEFINITIONS
476         INTERFACE_LINK_LIBRARIES
477      )
478         get_target_property( PROPS "${TARGET}" "${PROP}" )
479         if (PROPS)
480            set_target_properties(
481               "${INTERFACE_TARGET}"
482               PROPERTIES "${PROP}" "${PROPS}" )
483         endif()
484      endforeach()
485   endif()
486
487   # collect dependency information
488   list( APPEND GRAPH_EDGES "\"${TARGET}\" [${ATTRIBUTES}]" )
489   if (NOT LIBTYPE STREQUAL "MODULE")
490      list( APPEND GRAPH_EDGES "\"Audacity\" -> \"${TARGET}\"" )
491   endif ()
492   set(ACCESS PUBLIC PRIVATE INTERFACE)
493   foreach( IMPORT ${IMPORT_TARGETS} )
494      if(IMPORT IN_LIST ACCESS)
495         continue()
496      endif()
497      canonicalize_node_name(IMPORT "${IMPORT}")
498      list( APPEND GRAPH_EDGES "\"${TARGET}\" -> \"${IMPORT}\"" )
499   endforeach()
500   set( GRAPH_EDGES "${GRAPH_EDGES}" PARENT_SCOPE )
501endfunction()
502
503# Set up for defining a module target.
504# All modules depend on the application.
505# Pass a name and sources, and a list of other targets.
506# Use the interface compile definitions and include directories of the
507# other targets, and link to them.
508# More defines, and more target libraries (maybe generator expressions)
509# may be given too.
510macro( audacity_module NAME SOURCES IMPORT_TARGETS
511   ADDITIONAL_DEFINES ADDITIONAL_LIBRARIES )
512   # The extra indirection of a function call from this macro, and
513   # re-assignment of GRAPH_EDGES, is so that a module definition may
514   # call this macro, and it will (correctly) collect edges for the
515   # CMakeLists.txt in the directory above it; but otherwise we take
516   # advantage of function scoping of variables.
517   audacity_module_fn(
518      "${NAME}"
519      "${SOURCES}"
520      "${IMPORT_TARGETS}"
521      "${ADDITIONAL_DEFINES}"
522      "${ADDITIONAL_LIBRARIES}"
523      "MODULE"
524   )
525   set( GRAPH_EDGES "${GRAPH_EDGES}" PARENT_SCOPE )
526endmacro()
527
528# Set up for defining a library target.
529# The application depends on all libraries.
530# Pass a name and sources, and a list of other targets.
531# Use the interface compile definitions and include directories of the
532# other targets, and link to them.
533# More defines, and more target libraries (maybe generator expressions)
534# may be given too.
535macro( audacity_library NAME SOURCES IMPORT_TARGETS
536   ADDITIONAL_DEFINES ADDITIONAL_LIBRARIES )
537   # ditto comment in the previous macro
538   audacity_module_fn(
539      "${NAME}"
540      "${SOURCES}"
541      "${IMPORT_TARGETS}"
542      "${ADDITIONAL_DEFINES}"
543      "${ADDITIONAL_LIBRARIES}"
544      "SHARED"
545   )
546   set( GRAPH_EDGES "${GRAPH_EDGES}" PARENT_SCOPE )
547   # Collect list of libraries for the executable to declare dependency on
548   list( APPEND AUDACITY_LIBRARIES "${NAME}" )
549   set( AUDACITY_LIBRARIES "${AUDACITY_LIBRARIES}" PARENT_SCOPE )
550endmacro()
551
552# A special macro for header only libraries
553
554macro( audacity_header_only_library NAME SOURCES IMPORT_TARGETS
555   ADDITIONAL_DEFINES )
556   # ditto comment in the previous macro
557   add_library( ${NAME} INTERFACE )
558
559   target_include_directories ( ${NAME} INTERFACE ${CMAKE_CURRENT_SOURCE_DIR} )
560   target_sources( ${NAME} INTERFACE ${SOURCES})
561   target_link_libraries( ${NAME} INTERFACE ${IMPORT_TARGETS} )
562   target_compile_definitions( ${NAME} INTERFACE ${ADDITIONAL_DEFINES} )
563
564   list( APPEND AUDACITY_LIBRARIES "${NAME}" )
565   set( AUDACITY_LIBRARIES "${AUDACITY_LIBRARIES}" PARENT_SCOPE )
566endmacro()
567
568#
569# Add individual library targets
570#
571# Parms:
572#     dir      directory name within the cmake-proxies directory.
573#              (Doesn't HAVE to match the same directory in lib-src,
574#              but it usually does.)
575#
576#     name     suffix for the cmake user options
577#
578#     symbol   suffix for the "USE_<symbol>" variable that the Audacity
579#              target uses to include/exclude functionality.
580#
581#     required Determines if the library is required or not.  If it is,
582#              the user is not given the option of enabling/disabling it.
583#
584#     check    Determines if local/system checks should be performed here
585#              or in the subdirectory config.
586#
587#     packages A list of packages required for this target in pkg-config
588#              format.
589function( addlib dir name symbol required check )
590   set( subdir "${CMAKE_SOURCE_DIR}/cmake-proxies/${dir}" )
591   set( bindir "${CMAKE_BINARY_DIR}/cmake-proxies/${dir}" )
592
593   # Extract the list of packages from the function args
594   list( SUBLIST ARGV 5 -1 packages )
595
596   # Define target's name and it's source directory
597   set( TARGET ${dir} )
598   set( TARGET_ROOT ${libsrc}/${dir} )
599
600   # Define the option name
601   set( use ${_OPT}use_${name} )
602
603   # If we're not checking for system or local here, then let the
604   # target config handle the rest.
605   if( NOT check )
606      add_subdirectory( ${subdir} ${bindir} EXCLUDE_FROM_ALL )
607      return()
608   endif()
609
610   # If the target isn't required, allow the user to select which one
611   # to use or disable it entirely
612   set( desc "local" )
613   if( packages )
614      set( sysopt "system" )
615      string( PREPEND desc "system (if available), " )
616      set( default "${${_OPT}lib_preference}" )
617   else()
618      set( default "local" )
619   endif()
620
621   if( NOT required )
622      set( reqopt "off" )
623      string( APPEND desc ", off" )
624   endif()
625
626   cmd_option( ${use}
627               "Use ${name} library [${desc}]"
628               "${default}"
629               STRINGS ${sysopt} "local" ${reqopt}
630   )
631
632   # Bail if the target will not be used
633   if( ${use} STREQUAL "off" )
634      message( STATUS "========== ${name} disabled ==========" )
635
636      set( USE_${symbol} OFF CACHE INTERNAL "" FORCE )
637
638      return()
639   endif()
640
641   # Let the Audacity target know that this library will be used
642   set( USE_${symbol} ON CACHE INTERNAL "" FORCE )
643
644   if ( TARGET "${TARGET}" )
645      return()
646   endif()
647
648   message( STATUS "========== Configuring ${name} ==========" )
649
650   # Check for the system package(s) if the user prefers it
651   if( ${use} STREQUAL "system" )
652      # Look them up
653      pkg_check_modules( PKG_${TARGET} ${packages} )
654
655      if( PKG_${TARGET}_FOUND )
656         message( STATUS "Using '${name}' system library" )
657
658         # Create the target interface library
659         add_library( ${TARGET} INTERFACE IMPORTED GLOBAL )
660
661         # Retrieve the package information
662         get_package_interface( PKG_${TARGET} ${TARGET} )
663      else()
664         find_package( ${packages} QUIET )
665
666         if( TARGET ${TARGET} )
667            set( PKG_${TARGET}_FOUND Yes )
668         endif()
669      endif()
670
671      if( NOT PKG_${TARGET}_FOUND )
672         if( ${_OPT}obey_system_dependencies )
673            message( FATAL_ERROR "Failed to find the system package ${name}" )
674         else()
675            set( ${use} "local" )
676            set_property( CACHE ${use} PROPERTY VALUE "local" )
677         endif()
678      endif()
679   endif()
680
681   # User wants the local package or the system one wasn't found
682   if( ${use} STREQUAL "local" )
683      message( STATUS "Using '${name}' local library" )
684
685      # Pull in the target config
686      add_subdirectory( ${subdir} ${bindir} EXCLUDE_FROM_ALL )
687
688      # Get the list of targets defined by that config
689      get_property( targets DIRECTORY "${subdir}" PROPERTY BUILDSYSTEM_TARGETS )
690
691      # Set the folder (for the IDEs) for each one
692      foreach( target ${targets} )
693         # Skip interface libraries since they don't have any source to
694         # present in the IDEs
695         get_target_property( type "${target}" TYPE )
696         if( NOT "${type}" STREQUAL "INTERFACE_LIBRARY" )
697            set_target_properties( ${target} PROPERTIES FOLDER "lib-src" )
698         endif()
699      endforeach()
700   endif()
701endfunction()
702
703