diff --git a/lib/pkgconfig/nxdk_automount_d.pc b/lib/pkgconfig/nxdk_automount_d.pc new file mode 100644 index 000000000..d3ee6cef0 --- /dev/null +++ b/lib/pkgconfig/nxdk_automount_d.pc @@ -0,0 +1,7 @@ +Name: nxdk_automount_d +Description: Automatic mounting of the "D:" for nxdk XBEs. +Version: 1.0.0 +Requires: +Conflicts: +Libs: -l${NXDK_DIR}/lib/libnxdk_automount_d.lib +Cflags: diff --git a/lib/pkgconfig/nxdk_net.pc b/lib/pkgconfig/nxdk_net.pc new file mode 100644 index 000000000..4fbd9f901 --- /dev/null +++ b/lib/pkgconfig/nxdk_net.pc @@ -0,0 +1,7 @@ +Name: nxdk_net +Description: Network functionality for the NXDK +Version: 1.0.0 +Requires: +Conflicts: +Libs: -l${NXDK_DIR}/lib/libnxdk_net.lib +Cflags: -I${NXDK_DIR}/lib/net/lwip/src/include -I${NXDK_DIR}/lib/net/nforceif/include -I${NXDK_DIR}/lib/net/nvnetdrv diff --git a/lib/pkgconfig/pbkit.pc b/lib/pkgconfig/pbkit.pc new file mode 100644 index 000000000..561cdda81 --- /dev/null +++ b/lib/pkgconfig/pbkit.pc @@ -0,0 +1,7 @@ +Name: pbkit +Description: Pushbuffer management +Version: 1.0.0 +Requires: +Conflicts: +Libs: -l${NXDK_DIR}/lib/libpbkit.lib +Cflags: diff --git a/lib/pkgconfig/winmm.pc b/lib/pkgconfig/winmm.pc new file mode 100644 index 000000000..d65d1c5f6 --- /dev/null +++ b/lib/pkgconfig/winmm.pc @@ -0,0 +1,7 @@ +Name: winmm +Description: WinAPI multimedia extensions +Version: 1.0.0 +Requires: +Conflicts: +Libs: -l${NXDK_DIR}/lib/winmm.lib +Cflags: diff --git a/share/toolchain-nxdk.cmake b/share/toolchain-nxdk.cmake index 9de3a2610..3e1c706d1 100644 --- a/share/toolchain-nxdk.cmake +++ b/share/toolchain-nxdk.cmake @@ -1,8 +1,8 @@ -if(DEFINED ENV{NXDK_DIR}) - set(NXDK_DIR $ENV{NXDK_DIR}) -else() - message(FATAL_ERROR "The environment variable NXDK_DIR needs to be defined.") -endif() +get_filename_component(NXDK_ROOT_DIR "${CMAKE_CURRENT_LIST_DIR}/.." ABSOLUTE) +set(NXDK_DIR "${NXDK_ROOT_DIR}" CACHE PATH "Path to the nxdk root directory.") +message(STATUS "Using NXDK from: ${NXDK_DIR}") + +set(ENV{NXDK_DIR} "${NXDK_DIR}") set(CMAKE_SYSTEM_NAME Generic) set(CMAKE_SYSTEM_VERSION 1) @@ -18,6 +18,8 @@ set(WIN32 1) set(NXDK 1) +set(CMAKE_ASM_COMPILER "${NXDK_DIR}/bin/${TOOLCHAIN_PREFIX}-as") + set(CMAKE_C_COMPILER "${NXDK_DIR}/bin/${TOOLCHAIN_PREFIX}-cc") set(CMAKE_C_COMPILER_AR "llvm-ar") set(CMAKE_C_COMPILER_RANLIB "llvm-ranlib") @@ -66,7 +68,7 @@ set(_CMAKE_C_IPO_SUPPORTED_BY_CMAKE YES) set(_CMAKE_C_IPO_MAY_BE_SUPPORTED_BY_COMPILER YES) set(CMAKE_C_COMPILE_OPTIONS_IPO -flto) -set(PKG_CONFIG_EXECUTABLE "${NXDK_DIR}/bin/nxdk-pkg-config" CACHE STRING "Path to pkg-config") +set(PKG_CONFIG_EXECUTABLE "${NXDK_DIR}/bin/${TOOLCHAIN_PREFIX}-pkg-config" CACHE STRING "Path to pkg-config") # Fix generation of ninja depfiles set(CMAKE_DEPFILE_FLAGS_C "-MD -MF .d") diff --git a/tests/cmake/test.sh b/tests/cmake/test.sh new file mode 100755 index 000000000..30f80bb46 --- /dev/null +++ b/tests/cmake/test.sh @@ -0,0 +1,42 @@ +#!/usr/bin/env bash + +set -eu +set -o pipefail + +SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )" +readonly SCRIPT_DIR + +NXDK_ROOT_DIR="$( realpath ${SCRIPT_DIR}/../.. )" +BIN_DIR="${NXDK_ROOT_DIR}/bin" +readonly BIN_DIR +SHARE_DIR="${NXDK_ROOT_DIR}/share" +readonly SHARE_DIR + +echo "Verify that building works without the NXDK_DIR environment variable set." +TOOLCHAIN_FILE="${SHARE_DIR}/toolchain-nxdk.cmake" +unset NXDK_DIR +pushd test_project &> /dev/null +rm -rf build-noenvvar &> /dev/null +cmake -DCMAKE_TOOLCHAIN_FILE="${TOOLCHAIN_FILE}" -S . -B build-noenvvar +cmake --build build-noenvvar +rm -rf build-noenvvar &> /dev/null +popd &> /dev/null +echo "Build without env-var completed!" + + +NXDK_CMAKE="${BIN_DIR}/nxdk-cmake" +readonly NXDK_CMAKE + +echo "Verify that building works with the NXDK_DIR environment variable set." +pushd "${BIN_DIR}" &> /dev/null +. activate -s +popd &> /dev/null + +pushd test_project &> /dev/null +rm -rf build-envvar &> /dev/null +"${NXDK_CMAKE}" -S . -B build-envvar +cmake --build build-envvar +rm -rf build-envvar &> /dev/null +popd &> /dev/null + +echo "Build with env-var completed!" diff --git a/tests/cmake/test_project/CMakeLists.txt b/tests/cmake/test_project/CMakeLists.txt new file mode 100644 index 000000000..cb5dd957b --- /dev/null +++ b/tests/cmake/test_project/CMakeLists.txt @@ -0,0 +1,68 @@ +cmake_minimum_required(VERSION 3.30) + +project(nxdk_cmake_test_project LANGUAGES CXX C ASM) + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(_CMAKE_PROCESSING_LANGUAGE "CXX") +set(CMAKE_VERBOSE_MAKEFILE ON) + +if (NOT CMAKE_TOOLCHAIN_FILE MATCHES "toolchain-nxdk.cmake") + message(FATAL_ERROR "This project must be built with the nxdk toolchain (`-DCMAKE_TOOLCHAIN_FILE=/share/toolchain-nxdk.cmake`)") +endif () + +include(FindPkgConfig) + +list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/cmake/modules") +include(XBEUtils REQUIRED) + +pkg_check_modules( + nxdk_packages + REQUIRED + SDL2_image + SDL2_ttf + libjpeg + libpng + nxdk_automount_d + nxdk_net + pbkit + sdl2 + winmm + zlib +) + +add_executable( + ${PROJECT_NAME} + src/main.cpp + src/c_file.c + src/asm_file.s +) + +target_include_directories( + ${PROJECT_NAME} + PRIVATE + ${nxdk_packages_INCLUDE_DIRS} +) + +target_link_libraries( + ${PROJECT_NAME} + PRIVATE + ${nxdk_packages_LIBRARIES} +) + +target_compile_options( + ${PROJECT_NAME} + PRIVATE + ${nxdk_packages_CFLAGS} +) + +# TODO: Determine if there is an alternative to allow handwritten assembly. +target_link_options(${PROJECT_NAME} PRIVATE "/SAFESEH:NO") + + +set(EXECUTABLE_BINARY "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}.exe") +add_xbe( + xbe_file "${EXECUTABLE_BINARY}" + TITLE "Useless test xbe" +) +add_xiso(${PROJECT_NAME}_xiso xbe_file) diff --git a/tests/cmake/test_project/cmake/modules/XBEUtils.cmake b/tests/cmake/test_project/cmake/modules/XBEUtils.cmake new file mode 100644 index 000000000..34422aced --- /dev/null +++ b/tests/cmake/test_project/cmake/modules/XBEUtils.cmake @@ -0,0 +1,156 @@ +# Provides: +# +# add_xbe( +# target executable_file +# [XBENAME ="default.xbe"] +# [TITLE ={target}] +# ) +# +# Generates an XBE file from the given executable_file. +# +# add_xiso(target xbe_target [XISO =.xiso]) +# Generates an xiso image for the given XBE. + +include(CMakeParseArguments) + +set(CXBE_TOOL_PATH "${NXDK_DIR}/tools/cxbe/cxbe") +set(EXTRACT_XISO_TOOL_PATH "${NXDK_DIR}/tools/extract-xiso/build/extract-xiso") + +# Makes each path in the given list into an absolute path. +function(_make_abs_paths list_name) + set(ret "") + foreach (src "${${list_name}}") + get_filename_component(abs "${src}" ABSOLUTE) + list(APPEND ret "${abs}") + endforeach () + set(${list_name} ${ret} PARENT_SCOPE) +endfunction() + +# split_debug(executable_target) +# +# Splits debugging information from the given `executable_target`, generating a companion file ending in ".debug.exe". +function(split_debug) + if (${ARGC} LESS 1) + message(FATAL_ERROR "Missing required 'executable_target' parameter.") + endif () + + set(executable_target "${ARGV0}") + set(exe_file "${CMAKE_CURRENT_BINARY_DIR}/${executable_target}.exe") + get_filename_component(exe_dirname "${CMAKE_CURRENT_BINARY_DIR}/${executable_target}" DIRECTORY) + get_filename_component(exe_basename "${executable_target}" NAME_WE) + set(output "${exe_dirname}/${exe_basename}.debug.exe") + + add_custom_command( + TARGET "${executable_target}" + POST_BUILD + COMMAND "${CMAKE_COMMAND}" -E copy "${exe_file}" "${output}" + COMMAND "${CMAKE_OBJCOPY}" --strip-debug "${exe_file}" + COMMAND "${CMAKE_OBJCOPY}" "--add-gnu-debuglink=${output}" "${exe_file}" + COMMENT "Splitting debug information to reduce binary size..." + VERBATIM + BYPRODUCTS "${output}" + ) +endfunction() + +# Adds an XBE file. +function(add_xbe) + cmake_parse_arguments( + PARSE_ARGV + 2 + "XBE" + "" + "XBENAME;TITLE" + "" + ) + + if (${ARGC} LESS 1) + message(FATAL_ERROR "Missing required 'target' parameter.") + elseif (${ARGC} LESS 2) + message(FATAL_ERROR "Missing required 'executable_file' parameter.") + endif () + + set(target "${ARGV0}") + set(exe_file "${ARGV1}") + + if (NOT XBE_XBENAME) + set(XBE_XBENAME default.xbe) + endif () + + if (NOT XBE_TITLE) + set(XBE_TITLE "${target}") + endif () + + set( + "${target}_XBE_STAGING_DIR" + "${CMAKE_CURRENT_BINARY_DIR}/xbe/${target}" + CACHE INTERNAL + "Directory into which the raw sources for an xiso have been placed." + ) + set(XBE_STAGING_DIR "${${target}_XBE_STAGING_DIR}") + + set( + "${target}_XBE_OUTPUT_PATH" + "${${target}_XBE_STAGING_DIR}/${XBE_XBENAME}" + CACHE INTERNAL + "XBE file that should be added to an xiso." + ) + set(XBE_OUTPUT_PATH "${${target}_XBE_OUTPUT_PATH}") + + add_custom_command( + OUTPUT "${XBE_STAGING_DIR}" + COMMAND "${CMAKE_COMMAND}" -E make_directory "${XBE_STAGING_DIR}" + ) + + file(MAKE_DIRECTORY "${XBE_STAGING_DIR}") + + add_custom_command( + OUTPUT "${XBE_OUTPUT_PATH}" + COMMAND "${CXBE_TOOL_PATH}" + "-TITLE:${XBE_TITLE}" + "-OUT:${XBE_OUTPUT_PATH}" + "${exe_file}" + DEPENDS "${exe_file}" + ) +endfunction() + +function(add_xiso) + cmake_parse_arguments( + PARSE_ARGV + 2 + "XISO" + "" + "XISO" + "" + ) + + if (${ARGC} LESS 1) + message(FATAL_ERROR "Missing required 'target' parameter.") + elseif (${ARGC} LESS 2) + message(FATAL_ERROR "Missing required 'xbe_target' parameter.") + endif () + set(target "${ARGV0}") + set(xbe_target "${ARGV1}") + + if (NOT XISO_XISO) + set(XISO_XISO "${target}.iso") + endif () + + set(XBE_STAGING_DIR "${${xbe_target}_XBE_STAGING_DIR}") + set(XBE_OUTPUT_PATH "${${xbe_target}_XBE_OUTPUT_PATH}") + set(XISO_STAGING_DIR "${CMAKE_CURRENT_BINARY_DIR}/xiso/${target}") + file(MAKE_DIRECTORY "${XISO_STAGING_DIR}") + set(XISO_OUTPUT_PATH "${XISO_STAGING_DIR}/${XISO_XISO}") + + add_custom_command( + OUTPUT "${XISO_OUTPUT_PATH}" + COMMAND "${EXTRACT_XISO_TOOL_PATH}" -c "${XBE_STAGING_DIR}" "${XISO_OUTPUT_PATH}" + DEPENDS + "${XBE_OUTPUT_PATH}" + ) + + add_custom_target( + "${target}" + ALL + DEPENDS + "${XISO_OUTPUT_PATH}") +endfunction() diff --git a/tests/cmake/test_project/src/asm_file.s b/tests/cmake/test_project/src/asm_file.s new file mode 100644 index 000000000..d2d741676 --- /dev/null +++ b/tests/cmake/test_project/src/asm_file.s @@ -0,0 +1,14 @@ +.globl _add_ten + +# Define the start of the text/code section +.text + +_add_ten: + pushl %ebp + movl %esp, %ebp + + movl 8(%ebp), %eax + addl $10, %eax + + leave + ret diff --git a/tests/cmake/test_project/src/c_file.c b/tests/cmake/test_project/src/c_file.c new file mode 100644 index 000000000..aae29446d --- /dev/null +++ b/tests/cmake/test_project/src/c_file.c @@ -0,0 +1,5 @@ +#include + +BOOL verify_c_file_builds() { + return XVideoSetMode(640, 480, 32, REFRESH_DEFAULT); +} diff --git a/tests/cmake/test_project/src/c_file.h b/tests/cmake/test_project/src/c_file.h new file mode 100644 index 000000000..e89d5332c --- /dev/null +++ b/tests/cmake/test_project/src/c_file.h @@ -0,0 +1,14 @@ +#ifndef NXDK_TESTS_CMAKE_TEST_PROJECT_C_FILE_H_ +#define NXDK_TESTS_CMAKE_TEST_PROJECT_C_FILE_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +BOOL verify_c_file_builds(); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // NXDK_TESTS_CMAKE_TEST_PROJECT_C_FILE_H_ diff --git a/tests/cmake/test_project/src/main.cpp b/tests/cmake/test_project/src/main.cpp new file mode 100644 index 000000000..fbf5cfa6a --- /dev/null +++ b/tests/cmake/test_project/src/main.cpp @@ -0,0 +1,32 @@ + +#include +#include +#include +#include +#include + +#include "c_file.h" + +extern "C" int add_ten(int val); + +//! WARNING: This file is only intended to verify that a full-featured build is +//! achievable. Do not use this as an example for building real apps, no error +//! checking is performed. +int main() { + verify_c_file_builds(); + + pb_init(); + + SDL_Init(SDL_INIT_GAMECONTROLLER); + TTF_Init(); + + IMG_Init(IMG_INIT_PNG | IMG_INIT_JPG); + + int foo = add_ten(100); + + nx_net_parameters_t network_config{NX_NET_AUTO, NX_NET_AUTO, 0, 0, 0, 0, 0}; + nxNetInit(&network_config); + + pb_kill(); + return 0; +}