# HG changeset patch # User unC0Rr # Date 1735654783 -3600 # Node ID 629d5123a979aa221c4c7ece231e07308d8dc6d3 # Parent 2b4f361e3891262bbb25f0eae26d69f671cf1318# Parent 85d7d6b710872d86097653abaa40b5e39fd4bcd4 Merge transitional_engine branch into default diff -r 2b4f361e3891 -r 629d5123a979 .gitignore --- a/.gitignore Sat Sep 28 22:27:13 2024 +0200 +++ b/.gitignore Tue Dec 31 15:19:43 2024 +0100 @@ -32,7 +32,7 @@ misc/libphysfs/Xcode/build/ misc/libphyslayer/Xcode/build/ moc_*.cxx_parameters -relre:^release\/ +relre:^release/ *.log *.cmd *.diff diff -r 2b4f361e3891 -r 629d5123a979 CMakeLists.txt --- a/CMakeLists.txt Sat Sep 28 22:27:13 2024 +0200 +++ b/CMakeLists.txt Tue Dec 31 15:19:43 2024 +0100 @@ -1,9 +1,7 @@ -cmake_minimum_required(VERSION 2.6.4) - -project(hedgewars) +cmake_minimum_required(VERSION 3.12.0) #initialise cmake environment -foreach(hwpolicy CMP0003 CMP0012 CMP0017 CMP0018) +foreach(hwpolicy CMP0003 CMP0012 CMP0017 CMP0018 CMP0048) if(POLICY ${hwpolicy}) cmake_policy(SET ${hwpolicy} NEW) endif() @@ -15,6 +13,8 @@ endif() endforeach() +project(hedgewars VERSION 1.1.0) + set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake_modules") include(${CMAKE_MODULE_PATH}/utils.cmake) @@ -89,14 +89,11 @@ set(FONTS_DIRS "" CACHE STRING "Additional paths to folders where required fonts can be found ( ; is separator)") #versioning -set(CPACK_PACKAGE_VERSION_MAJOR 1) -set(CPACK_PACKAGE_VERSION_MINOR 1) -set(CPACK_PACKAGE_VERSION_PATCH 0) set(HEDGEWARS_PROTO_VER 60) if((CMAKE_BUILD_TYPE STREQUAL "Release") OR (CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo")) - set(HEDGEWARS_VERSION "${CPACK_PACKAGE_VERSION_MAJOR}.${CPACK_PACKAGE_VERSION_MINOR}.${CPACK_PACKAGE_VERSION_PATCH}") + set(HEDGEWARS_VERSION "${CMAKE_PROJECT_VERSION_MAJOR}.${CMAKE_PROJECT_VERSION_MINOR}.${CMAKE_PROJECT_VERSION_PATCH}") else() - set(HEDGEWARS_VERSION "${CPACK_PACKAGE_VERSION_MAJOR}.${CPACK_PACKAGE_VERSION_MINOR}.${CPACK_PACKAGE_VERSION_PATCH}-dev") + set(HEDGEWARS_VERSION "${CMAKE_PROJECT_VERSION_MAJOR}.${CMAKE_PROJECT_VERSION_MINOR}.${CMAKE_PROJECT_VERSION_PATCH}-dev") endif() include(${CMAKE_MODULE_PATH}/revinfo.cmake) @@ -109,7 +106,6 @@ #platform specific init code include(${CMAKE_MODULE_PATH}/platform.cmake) - #when build type is not specified, assume Debug/Release according to build version information if(CMAKE_BUILD_TYPE) if(NOT((CMAKE_BUILD_TYPE STREQUAL "Release") OR @@ -139,10 +135,10 @@ add_flag_append(CMAKE_CXX_FLAGS_DEBUG "/Od") else() add_flag_append(CMAKE_C_FLAGS "-Wall -pipe") - add_flag_append(CMAKE_C_FLAGS_RELEASE "-O2") + add_flag_append(CMAKE_C_FLAGS_RELEASE "-O3") add_flag_append(CMAKE_C_FLAGS_DEBUG "-Wextra -O0") add_flag_append(CMAKE_CXX_FLAGS "-Wall -pipe") - add_flag_append(CMAKE_CXX_FLAGS_RELEASE "-O2") + add_flag_append(CMAKE_CXX_FLAGS_RELEASE "-O3") add_flag_append(CMAKE_CXX_FLAGS_DEBUG "-Wextra -O0") endif() @@ -265,9 +261,17 @@ find_package_or_disable_msg(LIBAV NOVIDEOREC "Video recording will not be built") + #physfs helper library add_subdirectory(misc/libphyslayer) + +#rust libraries +add_subdirectory(tools/corrosion) +corrosion_import_crate(MANIFEST_PATH rust/lib-hwengine-future/Cargo.toml) +corrosion_install(TARGETS hwengine_future EXPORT hwengine_future_install_target) +install(EXPORT hwengine_future_install_target DESTINATION ${target_library_install_dir}) + #maybe this could be merged inside hedgewars/CMakeLists.txt if(BUILD_ENGINE_C) #pascal to c converter diff -r 2b4f361e3891 -r 629d5123a979 QTfrontend/CMakeLists.txt --- a/QTfrontend/CMakeLists.txt Sat Sep 28 22:27:13 2024 +0200 +++ b/QTfrontend/CMakeLists.txt Tue Dec 31 15:19:43 2024 +0100 @@ -19,6 +19,7 @@ else() find_package(SDL2_mixer 2 REQUIRED) #audio in SDLInteraction endif() + include_directories(${SDL2_INCLUDE_DIRS}) include_directories(${SDL2_MIXER_INCLUDE_DIRS}) diff -r 2b4f361e3891 -r 629d5123a979 QTfrontend/model/MapModel.h --- a/QTfrontend/model/MapModel.h Sat Sep 28 22:27:13 2024 +0200 +++ b/QTfrontend/model/MapModel.h Tue Dec 31 15:19:43 2024 +0100 @@ -46,28 +46,30 @@ Q_OBJECT public: - enum MapType { - Invalid, - GeneratedMap, - GeneratedMaze, - GeneratedPerlin, - HandDrawnMap, - MissionMap, - StaticMap, - FortsMap - }; + enum MapType { + Invalid, + GeneratedMap, + GeneratedMaze, + GeneratedPerlin, + HandDrawnMap, + MissionMap, + StaticMap, + FortsMap, + WfcMap + }; - /// a struct for holding the attributes of a map. - struct MapInfo - { - MapType type; ///< The map-type - QString name; ///< The internal name. - QString theme; ///< The theme to be used. (can be empty) - quint32 limit; ///< The maximum allowed number of hedgehogs. - QString scheme; ///< Default scheme name or "locked", for mission-maps. - QString weapons; ///< Default weaponset name or "locked", for missions-maps. - QString desc; ///< The brief 1-2 sentence description of the mission, for mission-maps. - bool dlc; ///< True if this map was not packaged with the game + /// a struct for holding the attributes of a map. + struct MapInfo { + MapType type; ///< The map-type + QString name; ///< The internal name. + QString theme; ///< The theme to be used. (can be empty) + quint32 limit; ///< The maximum allowed number of hedgehogs. + QString scheme; ///< Default scheme name or "locked", for mission-maps. + QString + weapons; ///< Default weaponset name or "locked", for missions-maps. + QString desc; ///< The brief 1-2 sentence description of the mission, + ///< for mission-maps. + bool dlc; ///< True if this map was not packaged with the game }; MapModel(MapType maptype, QObject *parent = 0); diff -r 2b4f361e3891 -r 629d5123a979 QTfrontend/net/hwmap.h --- a/QTfrontend/net/hwmap.h Sat Sep 28 22:27:13 2024 +0200 +++ b/QTfrontend/net/hwmap.h Tue Dec 31 15:19:43 2024 +0100 @@ -26,14 +26,14 @@ #include "tcpBase.h" -enum MapGenerator -{ - MAPGEN_REGULAR = 0, - MAPGEN_MAZE = 1, - MAPGEN_PERLIN = 2, - MAPGEN_DRAWN = 3, - MAPGEN_FORTS = 4, - MAPGEN_MAP = 5 +enum MapGenerator { + MAPGEN_REGULAR = 0, + MAPGEN_MAZE = 1, + MAPGEN_PERLIN = 2, + MAPGEN_DRAWN = 3, + MAPGEN_FORTS = 4, + MAPGEN_WFC = 5, + MAPGEN_MAP = 6, }; class HWMap : public TCPBase diff -r 2b4f361e3891 -r 629d5123a979 QTfrontend/ui/widget/mapContainer.cpp --- a/QTfrontend/ui/widget/mapContainer.cpp Sat Sep 28 22:27:13 2024 +0200 +++ b/QTfrontend/ui/widget/mapContainer.cpp Tue Dec 31 15:19:43 2024 +0100 @@ -129,7 +129,9 @@ cType->insertItem(4, tr("Random maze"), MapModel::GeneratedMaze); cType->insertItem(5, tr("Random perlin"), MapModel::GeneratedPerlin); cType->insertItem(6, tr("Forts"), MapModel::FortsMap); - connect(cType, SIGNAL(currentIndexChanged(int)), this, SLOT(mapTypeChanged(int))); + cType->insertItem(7, tr("WFC"), MapModel::WfcMap); + connect(cType, SIGNAL(currentIndexChanged(int)), this, + SLOT(mapTypeChanged(int))); m_childWidgets << cType; /* Randomize button */ @@ -760,19 +762,23 @@ m_mapInfo.type = MapModel::FortsMap; f = true; break; + case MAPGEN_WFC: + m_mapInfo.type = MapModel::WfcMap; + f = true; + break; case MAPGEN_MAP: - switch (m_mapInfo.type) - { - case MapModel::GeneratedMap: - case MapModel::GeneratedMaze: - case MapModel::GeneratedPerlin: - case MapModel::HandDrawnMap: - case MapModel::FortsMap: - m_mapInfo.type = MapModel::Invalid; - default: - break; - } - break; + switch (m_mapInfo.type) { + case MapModel::GeneratedMap: + case MapModel::GeneratedMaze: + case MapModel::GeneratedPerlin: + case MapModel::HandDrawnMap: + case MapModel::FortsMap: + case MapModel::WfcMap: + m_mapInfo.type = MapModel::Invalid; + default: + break; + } + break; } if(f) @@ -948,10 +954,11 @@ case MapModel::GeneratedMap: case MapModel::GeneratedPerlin: case MapModel::GeneratedMaze: - mapPreview->setWhatsThis(randomAllPrev); - mapFeatureSize->setWhatsThis(mfsComplex); - btnRandomize->setWhatsThis(randomAll); - break; + case MapModel::WfcMap: + mapPreview->setWhatsThis(randomAllPrev); + mapFeatureSize->setWhatsThis(mfsComplex); + btnRandomize->setWhatsThis(randomAll); + break; case MapModel::MissionMap: case MapModel::StaticMap: mapPreview->setWhatsThis(randomAllPrev); @@ -1059,8 +1066,15 @@ setMapInfo(MapModel::MapInfoForts); lblMapList->hide(); break; + case MapModel::WfcMap: + mapgen = MAPGEN_WFC; + setMapInfo(MapModel::MapInfoRandom); + lblMapList->setText(tr("Map size:")); + lblMapList->show(); + generationStyles->show(); + break; default: - break; + break; } // Update theme button size diff -r 2b4f361e3891 -r 629d5123a979 hedgewars/CMakeLists.txt --- a/hedgewars/CMakeLists.txt Sat Sep 28 22:27:13 2024 +0200 +++ b/hedgewars/CMakeLists.txt Tue Dec 31 15:19:43 2024 +0100 @@ -1,10 +1,10 @@ enable_language(Pascal) find_package(SDL2 REQUIRED CONFIG) -find_package(SDL2_image REQUIRED CONFIG) -find_package(SDL2_net REQUIRED CONFIG) -find_package(SDL2_ttf REQUIRED CONFIG) -find_package(SDL2_mixer REQUIRED CONFIG) +find_package(SDL2_image REQUIRED) +find_package(SDL2_net REQUIRED) +find_package(SDL2_ttf REQUIRED) +find_package(SDL2_mixer REQUIRED) include(CheckLibraryExists) include(${CMAKE_MODULE_PATH}/utils.cmake) @@ -78,13 +78,11 @@ adler32.pas uLandTemplates.pas uLandTexture.pas + uLandGenPerlin.pas uLandGraphics.pas uLandPainted.pas - uLandOutline.pas - uLandGenMaze.pas - uLandGenPerlin.pas - uLandGenTemplateBased.pas uLandUtils.pas + uAI2.pas #this is where dependency tracking becomes hard uStore.pas @@ -189,7 +187,6 @@ list(APPEND HW_LINK_LIBS physlayer) - #opengl 2 IF(GL2) add_definitions(-dGL2) @@ -246,6 +243,6 @@ endif() #even though not actually used, this will trigger relink if any lib changes -target_link_libraries(hwengine ${HW_LINK_LIBS}) +target_link_libraries(hwengine ${HW_LINK_LIBS} hwengine_future) install(PROGRAMS "${EXECUTABLE_OUTPUT_PATH}/${engine_output_name}" DESTINATION ${destination_dir}) diff -r 2b4f361e3891 -r 629d5123a979 hedgewars/SDLh.pas --- a/hedgewars/SDLh.pas Sat Sep 28 22:27:13 2024 +0200 +++ b/hedgewars/SDLh.pas Tue Dec 31 15:19:43 2024 +0100 @@ -931,6 +931,9 @@ TLongWordArray = array[0..16383] of LongWord; PLongWordArray = ^TLongWordArray; + TWordArray = array[0..16383] of Word; + PWordArray = ^TWordArray; + PSDL_Thread = Pointer; PSDL_mutex = Pointer; PSDL_sem = Pointer; @@ -1033,14 +1036,14 @@ {$IFDEF WINDOWS} TThreadFunction = function (p: pointer): Longword; stdcall; pfnSDL_CurrentBeginThread = function ( - _Security: pointer; + _Security: pointer; _StackSize: LongWord; _StartAddress: TThreadFunction; _ArgList: pointer; _InitFlag: Longword; _ThrdAddr: PLongword): PtrUInt; cdecl; pfnSDL_CurrentEndThread = procedure (_Retval: LongInt); cdecl; -{$ENDIF} +{$ENDIF} ///////////////////////////////////////////////////////////////// ///////////////////// FUNCTION DEFINITIONS ///////////////////// @@ -1142,7 +1145,7 @@ procedure SDL_SetEventFilter(filter: TSDL_EventFilter); cdecl; external SDLLibName; function SDL_ShowCursor(toggle: LongInt): LongInt; cdecl; external SDLLibName; -procedure SDL_WarpMouse(x, y: Word); inline; +procedure SDL_WarpMouse(x, y: Word); function SDL_GetKeyboardState(numkeys: PLongInt): PByteArray; cdecl; external SDLLibName; @@ -1297,7 +1300,7 @@ // for sdl2 we provide a SDL_WarpMouse() which calls the right SDL_WarpMouseInWindow() function // this has the advantage of reducing 'uses' and 'ifdef' statements // (SDLwindow is a private member of uStore module) -procedure SDL_WarpMouse(x, y: Word); inline; +procedure SDL_WarpMouse(x, y: Word); begin WarpMouse(x, y); end; @@ -1340,7 +1343,7 @@ function SDL_CreateThread(fn: Pointer; name: PChar; data: Pointer): PSDL_Thread; cdecl; begin SDL_CreateThread:= SDL_CreateThread(fn, name, data, nil, nil) -end; +end; {$ENDIF} end. diff -r 2b4f361e3891 -r 629d5123a979 hedgewars/options.inc --- a/hedgewars/options.inc Sat Sep 28 22:27:13 2024 +0200 +++ b/hedgewars/options.inc Tue Dec 31 15:19:43 2024 +0100 @@ -65,6 +65,8 @@ {$DEFINE _S:=} {$DEFINE _P:=} +{$optimization autoInline} + //{$DEFINE TRACEAIACTIONS} //{$DEFINE COUNTTICKS} diff -r 2b4f361e3891 -r 629d5123a979 hedgewars/uAI.pas --- a/hedgewars/uAI.pas Sat Sep 28 22:27:13 2024 +0200 +++ b/hedgewars/uAI.pas Tue Dec 31 15:19:43 2024 +0100 @@ -32,7 +32,7 @@ uses uConsts, SDLh, uAIMisc, uAIAmmoTests, uAIActions, uAmmos, uTypes, uVariables, uCommands, uUtils, uDebug, uAILandMarks, - uGearsUtils; + uGearsUtils, uAI2; var BestActions: TActions; CanUseAmmo: array [TAmmoType] of boolean; @@ -635,7 +635,7 @@ var scoreShown: boolean = false; {$ENDIF} -procedure ProcessBot; +procedure ProcessBot_old; const cStopThinkTime = 40; begin with CurrentHedgehog^ do @@ -677,6 +677,12 @@ StopThinking:= true end; + +procedure ProcessBot; +begin + if true then ProcessBot_old else uAI2.ProcessBot +end; + procedure initModule; begin StartTicks:= 0; diff -r 2b4f361e3891 -r 629d5123a979 hedgewars/uAI2.pas --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hedgewars/uAI2.pas Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,60 @@ +unit uAI2; + +interface + +procedure ProcessBot; +procedure initModule; + +implementation +uses uLandUtils, uFloat, uVariables, uTypes, uAmmos; + +{$linklib hwengine_future} + +type TAmmoCounts = array[TAmmoType] of Longword; + PAmmoCounts = ^TAmmoCounts; + +function create_ai(game_field: pointer): pointer; cdecl; external; +procedure ai_clear_team(ai: pointer); cdecl; external; +procedure ai_add_team_hedgehog(ai: pointer; x, y: real; ammo_counts: PAmmoCounts); cdecl; external; +procedure ai_think(ai: pointer); cdecl; external; +function ai_have_plan(): boolean; cdecl; external; +procedure dispose_ai(ai: pointer); cdecl; external; + +var ai: pointer; + +procedure ProcessBot; +var currHedgehogIndex, itHedgehog: Longword; + itAmmo: TAmmoType; + ammoCounts: TAmmoCounts; +begin + if ai = nil then + begin + ai:= create_ai(gameField) + end; + + ai_clear_team(ai); + + currHedgehogIndex:= CurrentTeam^.CurrHedgehog; + itHedgehog:= currHedgehogIndex; + repeat + with CurrentTeam^.Hedgehogs[itHedgehog] do + if (Gear <> nil) and (Effects[heFrozen] = 0) then + begin + for itAmmo:= Low(TAmmoType) to High(TAmmoType) do + ammoCounts[itAmmo]:= HHHasAmmo(CurrentTeam^.Hedgehogs[itHedgehog], itAmmo); + + ai_add_team_hedgehog(ai, hwFloat2float(Gear^.X), hwFloat2float(Gear^.Y), @ammoCounts) + end; + itHedgehog:= Succ(itHedgehog) mod CurrentTeam^.HedgehogsNumber; + until (itHedgehog = currHedgehogIndex); + + ai_think(ai); +end; + +procedure initModule; +begin + ai:= nil +end; + +end. + diff -r 2b4f361e3891 -r 629d5123a979 hedgewars/uAIActions.pas --- a/hedgewars/uAIActions.pas Sat Sep 28 22:27:13 2024 +0200 +++ b/hedgewars/uAIActions.pas Tue Dec 31 15:19:43 2024 +0100 @@ -120,7 +120,17 @@ {$ENDIF} procedure AddAction(var Actions: TActions; Action: Longword; Param: LongInt; TimeDelta: Longword; X, Y: LongInt); +var t: Longword; begin +if ((Action = aia_LookLeft) or (Action = aia_LookRight)) and (Actions.Count > 0) then + begin + t:= Actions.actions[Actions.Count - 1].Action; + if ((t = aia_LookLeft) or (t = aia_LookRight)) then + begin + dec(Actions.Count) + end; + end; + if Actions.Count < MAXACTIONS then with Actions do begin @@ -136,16 +146,23 @@ end end; -procedure CheckHang(Me: PGear); +procedure CheckHang(Me: PGear; fromLeft: boolean); +var newX: LongInt; begin -if hwRound(Me^.X) <> PrevX then +newX:= hwRound(Me^.X); +if newX <> PrevX then begin - PrevX:= hwRound(Me^.X); + if (newX < PrevX) = fromLeft then + begin + FreeActionsList + end; + + PrevX:= newX; timedelta:= 0 end else begin inc(timedelta); - if timedelta > 1700 then + if timedelta > 900 then begin timedelta:= 0; FreeActionsList @@ -186,7 +203,7 @@ end else begin - CheckHang(Me); + CheckHang(Me, false); exit end; @@ -205,7 +222,7 @@ end else begin - CheckHang(Me); + CheckHang(Me, true); exit end; aia_LookLeft: begin diff -r 2b4f361e3891 -r 629d5123a979 hedgewars/uAIAmmoTests.pas --- a/hedgewars/uAIAmmoTests.pas Sat Sep 28 22:27:13 2024 +0200 +++ b/hedgewars/uAIAmmoTests.pas Tue Dec 31 15:19:43 2024 +0100 @@ -25,7 +25,7 @@ amtest_Rare = $00000001; // check only several positions amtest_NoTarget = $00000002; // each pos, but no targetting amtest_MultipleAttacks = $00000004; // test could result in multiple attacks, set AttacksNum - amtest_NoTrackFall = $00000008; // skip fall tracing. + amtest_NoTrackFall = $00000008; // skip fall tracing. amtest_LaserSight = $00000010; // supports laser sighting amtest_NoVampiric = $00000020; // don't use vampirism with this ammo amtest_NoInvulnerable = $00000040; // don't use invulnerable with this with ammo @@ -151,9 +151,9 @@ ); implementation -uses uVariables, uUtils, uGearsHandlers; +uses uVariables, uUtils, uGearsHandlers, uLandUtils; -function Metric(x1, y1, x2, y2: LongInt): LongInt; inline; +function Metric(x1, y1, x2, y2: LongInt): LongInt; begin Metric:= abs(x1 - x2) + abs(y1 - y2) end; @@ -1081,7 +1081,7 @@ if ((ix and LAND_WIDTH_MASK) = 0) and ((iy and LAND_HEIGHT_MASK) = 0) then repeat - if Land[iy, ix] <> 0 then + if LandGet(iy, ix) <> 0 then inc(d); x:= x + vX; y:= y + vY; @@ -1137,7 +1137,7 @@ x:= x + vX; y:= y + vY; if ((trunc(x) and LAND_WIDTH_MASK) = 0)and((trunc(y) and LAND_HEIGHT_MASK) = 0) - and (Land[trunc(y), trunc(x)] <> 0) then + and (LandGet(trunc(y), trunc(x)) <> 0) then inc(d); until (Abs(Targ.Point.X - trunc(x)) + Abs(Targ.Point.Y - trunc(y)) < 4) or (x < 0) diff -r 2b4f361e3891 -r 629d5123a979 hedgewars/uAIMisc.pas --- a/hedgewars/uAIMisc.pas Sat Sep 28 22:27:13 2024 +0200 +++ b/hedgewars/uAIMisc.pas Tue Dec 31 15:19:43 2024 +0100 @@ -71,20 +71,20 @@ procedure freeModule; procedure FillTargets; -procedure ResetTargets; inline; -procedure AddBonus(x, y: LongInt; r: Longword; s: LongInt); inline; +procedure ResetTargets; +procedure AddBonus(x, y: LongInt; r: Longword; s: LongInt); procedure FillBonuses(isAfterAttack: boolean); -procedure AwareOfExplosion(x, y, r: LongInt); inline; +procedure AwareOfExplosion(x, y, r: LongInt); function RatePlace(Gear: PGear): LongInt; -function CheckWrap(x: real): real; inline; -function TestColl(x, y, r: LongInt): boolean; inline; -function TestCollHogsOrObjects(x, y, r: LongInt): boolean; inline; -function TestCollExcludingObjects(x, y, r: LongInt): boolean; inline; -function TestCollExcludingMe(Me: PGear; x, y, r: LongInt): boolean; inline; +function CheckWrap(x: real): real; +function TestColl(x, y, r: LongInt): boolean; +function TestCollHogsOrObjects(x, y, r: LongInt): boolean; +function TestCollExcludingObjects(x, y, r: LongInt): boolean; +function TestCollExcludingMe(Me: PGear; x, y, r: LongInt): boolean; -function RateExplosion(Me: PGear; x, y, r: LongInt): LongInt; inline; -function RateExplosion(Me: PGear; x, y, r: LongInt; Flags: LongWord): LongInt; inline; +function RateExplosion(Me: PGear; x, y, r: LongInt): LongInt; +function RateExplosion(Me: PGear; x, y, r: LongInt; Flags: LongWord): LongInt; function RealRateExplosion(Me: PGear; x, y, r: LongInt; Flags: LongWord): LongInt; function RateShove(Me: PGear; x, y, r, power, kick: LongInt; gdX, gdY: real; Flags: LongWord): LongInt; function RateShotgun(Me: PGear; gdX, gdY: real; x, y: LongInt): LongInt; @@ -93,8 +93,8 @@ function RateHammer(Me: PGear): LongInt; function HHGo(Gear, AltGear: PGear; var GoInfo: TGoInfo): boolean; -function AIrndSign(num: LongInt): LongInt; inline; -function AIrndOffset(targ: TTarget; Level: LongWord): LongInt; inline; +function AIrndSign(num: LongInt): LongInt; +function AIrndOffset(targ: TTarget; Level: LongWord): LongInt; var ThinkingHH: PGear; Targets: TTargets; @@ -109,14 +109,14 @@ var dmgMod: real = 1.0; implementation -uses uCollisions, uVariables, uUtils, uGearsUtils, uAIAmmoTests; +uses uCollisions, uVariables, uUtils, uGearsUtils, uAIAmmoTests, uLandUtils; var KnownExplosion: record X, Y, Radius: LongInt end = (X: 0; Y: 0; Radius: 0); -procedure ResetTargets; inline; +procedure ResetTargets; var i: LongWord; begin if Targets.reset then @@ -200,7 +200,7 @@ else friendlyfactor:= max(30, 300 - f * 80 div max(1,e)) end; -procedure AddBonus(x, y: LongInt; r: Longword; s: LongInt); inline; +procedure AddBonus(x, y: LongInt; r: Longword; s: LongInt); begin if(bonuses.Count < MAXBONUS) then begin @@ -212,7 +212,7 @@ end; end; -procedure AddWalkBonus(x, y: LongInt; r: Longword; s: LongInt); inline; +procedure AddWalkBonus(x, y: LongInt; r: Longword; s: LongInt); begin if(walkbonuses.Count < MAXBONUS div 8) then begin @@ -334,7 +334,7 @@ end; end; -procedure AwareOfExplosion(x, y, r: LongInt); inline; +procedure AwareOfExplosion(x, y, r: LongInt); begin KnownExplosion.X:= x; KnownExplosion.Y:= y; @@ -363,17 +363,17 @@ RatePlace:= rate; end; -function CheckWrap(x: real): real; inline; +function CheckWrap(x: real): real; begin if WorldEdge = weWrap then if (x < leftX) then x:= x + (rightX - leftX) - else if x > rightX then + else if x > rightX then x:= x - (rightX - leftX); CheckWrap:= x; end; -function CheckBounds(x, y, r: Longint): boolean; inline; +function CheckBounds(x, y, r: Longint): boolean; begin CheckBounds := (((x-r) and LAND_WIDTH_MASK) = 0) and (((x+r) and LAND_WIDTH_MASK) = 0) and @@ -383,60 +383,60 @@ // Check for collision with anything -function TestCollWithEverything(x, y, r: LongInt): boolean; inline; +function TestCollWithEverything(x, y, r: LongInt): boolean; begin if not CheckBounds(x, y, r) then exit(false); - if (Land[y-r, x-r] <> 0) or - (Land[y+r, x-r] <> 0) or - (Land[y-r, x+r] <> 0) or - (Land[y+r, x+r] <> 0) then + if (LandGet(y-r, x-r) <> 0) or + (LandGet(y+r, x-r) <> 0) or + (LandGet(y-r, x+r) <> 0) or + (LandGet(y+r, x+r) <> 0) then exit(true); TestCollWithEverything := false; end; // Check for collision with non-objects -function TestCollExcludingObjects(x, y, r: LongInt): boolean; inline; +function TestCollExcludingObjects(x, y, r: LongInt): boolean; begin if not CheckBounds(x, y, r) then exit(false); - if (Land[y-r, x-r] > lfAllObjMask) or - (Land[y+r, x-r] > lfAllObjMask) or - (Land[y-r, x+r] > lfAllObjMask) or - (Land[y+r, x+r] > lfAllObjMask) then + if (LandGet(y-r, x-r) > lfAllObjMask) or + (LandGet(y+r, x-r) > lfAllObjMask) or + (LandGet(y-r, x+r) > lfAllObjMask) or + (LandGet(y+r, x+r) > lfAllObjMask) then exit(true); TestCollExcludingObjects:= false; end; // Check for collision with something other than current hedgehog or crate -function TestColl(x, y, r: LongInt): boolean; inline; +function TestColl(x, y, r: LongInt): boolean; begin if not CheckBounds(x, y, r) then exit(false); - if (Land[y-r, x-r] and lfNotCurHogCrate <> 0) or - (Land[y+r, x-r] and lfNotCurHogCrate <> 0) or - (Land[y-r, x+r] and lfNotCurHogCrate <> 0) or - (Land[y+r, x+r] and lfNotCurHogCrate <> 0) then + if (LandGet(y-r, x-r) and lfNotCurHogCrate <> 0) or + (LandGet(y+r, x-r) and lfNotCurHogCrate <> 0) or + (LandGet(y-r, x+r) and lfNotCurHogCrate <> 0) or + (LandGet(y+r, x+r) and lfNotCurHogCrate <> 0) then exit(true); TestColl:= false; end; // Check for collision with hedgehog or object -function TestCollHogsOrObjects(x, y, r: LongInt): boolean; inline; +function TestCollHogsOrObjects(x, y, r: LongInt): boolean; begin if not CheckBounds(x, y, r) then exit(false); - if (Land[y-r, x-r] and lfAllObjMask <> 0) or - (Land[y+r, x-r] and lfAllObjMask <> 0) or - (Land[y-r, x+r] and lfAllObjMask <> 0) or - (Land[y+r, x+r] and lfAllObjMask <> 0) then + if (LandGet(y-r, x-r) and lfAllObjMask <> 0) or + (LandGet(y+r, x-r) and lfAllObjMask <> 0) or + (LandGet(y-r, x+r) and lfAllObjMask <> 0) or + (LandGet(y+r, x+r) and lfAllObjMask <> 0) then exit(true); TestCollHogsOrObjects:= false; @@ -445,7 +445,7 @@ // Check for collision with something other than the given "Me" gear. // Wrapper to test various approaches. If it works reasonably, will just replace. // Right now, converting to hwFloat is a tad inefficient since the x/y were hwFloat to begin with... -function TestCollExcludingMe(Me: PGear; x, y, r: LongInt): boolean; inline; +function TestCollExcludingMe(Me: PGear; x, y, r: LongInt): boolean; var MeX, MeY: LongInt; begin if ((x and LAND_WIDTH_MASK) = 0) and ((y and LAND_HEIGHT_MASK) = 0) then @@ -453,7 +453,7 @@ MeX:= hwRound(Me^.X); MeY:= hwRound(Me^.Y); // We are still inside the hog. Skip radius test - if ((sqr(x-MeX) + sqr(y-MeY)) < 256) and (Land[y, x] and lfObjMask = 0) then + if ((sqr(x-MeX) + sqr(y-MeY)) < 256) and (LandGet(y, x) and lfObjMask = 0) then exit(false); end; TestCollExcludingMe:= TestCollWithEverything(x, y, r) @@ -527,7 +527,7 @@ { if ((trunc(y) and LAND_HEIGHT_MASK) = 0) and ((trunc(x) and LAND_WIDTH_MASK) = 0) then begin - LandPixels[trunc(y), trunc(x)]:= v; + LandPixelGet(trunc(y), trunc(x)):= v; UpdateLandTexture(trunc(X), 1, trunc(Y), 1, true); end;} @@ -566,12 +566,12 @@ end; end; -function RateExplosion(Me: PGear; x, y, r: LongInt): LongInt; inline; +function RateExplosion(Me: PGear; x, y, r: LongInt): LongInt; begin RateExplosion:= RealRateExplosion(Me, x, y, r, 0); ResetTargets; end; -function RateExplosion(Me: PGear; x, y, r: LongInt; Flags: LongWord): LongInt; inline; +function RateExplosion(Me: PGear; x, y, r: LongInt; Flags: LongWord): LongInt; begin RateExplosion:= RealRateExplosion(Me, x, y, r, Flags); ResetTargets; @@ -637,7 +637,7 @@ if pY - y < 0 then dY:= -dY; if (x and LAND_WIDTH_MASK = 0) and ((y+cHHRadius+2) and LAND_HEIGHT_MASK = 0) and - (Land[y+cHHRadius+2, x] and lfIndestructible <> 0) then + (LandGet(y+cHHRadius+2, x) and lfIndestructible <> 0) then fallDmg:= trunc(TraceFall(x, y, pX, pY, dX, dY, 0, Targets.ar[i]) * dmgMod) else fallDmg:= trunc(TraceFall(x, y, pX, pY, dX, dY, erasure, Targets.ar[i]) * dmgMod) end; @@ -831,7 +831,7 @@ ((abs(dY) < 0.15) and (abs(dX) < 0.15))) then dX:= 0; if (x and LAND_WIDTH_MASK = 0) and ((y+cHHRadius+2) and LAND_HEIGHT_MASK = 0) and - (Land[y+cHHRadius+2, x] and lfIndestructible <> 0) then + (LandGet(y+cHHRadius+2, x) and lfIndestructible <> 0) then fallDmg:= trunc(TraceFall(x, y, pX, pY, dX, dY, 0, Targets.ar[i]) * dmgMod) else fallDmg:= trunc(TraceFall(x, y, pX, pY, dX, dY, erasure, Targets.ar[i]) * dmgMod) end; @@ -915,12 +915,12 @@ begin if (WorldEdge <> weWrap) or (not (hwAbs(meX - int2hwFloat(pX)) > int2hwFloat(cSeductionDist))) then - dX:= _50 * aiGravity * (meX - int2hwFloat(pX)) / _25 + dX:= aiGravity * (meX - int2hwFloat(pX)) / 2 else if (not (hwAbs(meX + int2hwFloat((RightX-LeftX) - pX)) > int2hwFloat(cSeductionDist))) then - dX:= _50 * aiGravity * (meX + (int2hwFloat((RightX-LeftX) - pX))) / _25 + dX:= aiGravity * (meX + (int2hwFloat((RightX-LeftX) - pX))) / 2 else - dX:= _50 * aiGravity * (meX - (int2hwFloat((RightX-LeftX) - pX))) / _25; - dY:= -_450 * cMaxWindSpeed * 2; + dX:= aiGravity * (meX - (int2hwFloat((RightX-LeftX) - pX))) / 2; + dY:= -_900 * cMaxWindSpeed; pXr:= pX; @@ -1064,7 +1064,7 @@ begin if TestCollisionYwithGear(Gear, -1) <> 0 then if TestCollisionXwithXYShift(Gear, _0, -2, hwSign(Gear^.dX)) = 0 then - Gear^.Y:= Gear^.Y - int2hwFloat(2) + Gear^.Y:= Gear^.Y - _2 else if TestCollisionXwithXYShift(Gear, _0, -1, hwSign(Gear^.dX)) = 0 then Gear^.Y:= Gear^.Y - _1; @@ -1081,12 +1081,6 @@ end; repeat - {if ((hwRound(Gear^.Y) and LAND_HEIGHT_MASK) = 0) and ((hwRound(Gear^.X) and LAND_WIDTH_MASK) = 0) then - begin - LandPixels[hwRound(Gear^.Y), hwRound(Gear^.X)]:= Gear^.Hedgehog^.Team^.Clan^.Color; - UpdateLandTexture(hwRound(Gear^.X), 1, hwRound(Gear^.Y), 1, true); - end;} - if CheckCoordInWater(hwRound(Gear^.X), hwRound(Gear^.Y) + cHHRadius) then exit(false); if (Gear^.State and gstMoving) <> 0 then @@ -1147,12 +1141,6 @@ GoInfo.JumpType:= jmpNone; tY:= hwRound(Gear^.Y); repeat - {if ((hwRound(Gear^.Y) and LAND_HEIGHT_MASK) = 0) and ((hwRound(Gear^.X) and LAND_WIDTH_MASK) = 0) then - begin - LandPixels[hwRound(Gear^.Y), hwRound(Gear^.X)]:= random($FFFFFFFF);//Gear^.Hedgehog^.Team^.Clan^.Color; - UpdateLandTexture(hwRound(Gear^.X), 1, hwRound(Gear^.Y), 1, true); - end;} - pX:= hwRound(Gear^.X); pY:= hwRound(Gear^.Y); if CheckCoordInWater(pX, pY + cHHRadius) then @@ -1206,7 +1194,7 @@ HHJump(AltGear, jmpHJump, GoInfo); end; -function AIrndSign(num: LongInt): LongInt; inline; +function AIrndSign(num: LongInt): LongInt; begin if random(2) = 0 then AIrndSign:= num @@ -1214,7 +1202,7 @@ AIrndSign:= - num end; -function AIrndOffset(targ: TTarget; Level: LongWord): LongInt; inline; +function AIrndOffset(targ: TTarget; Level: LongWord): LongInt; begin if Level <> 1 then exit(0); // at present level 2 doesn't track falls on most things diff -r 2b4f361e3891 -r 629d5123a979 hedgewars/uChat.pas --- a/hedgewars/uChat.pas Sat Sep 28 22:27:13 2024 +0200 +++ b/hedgewars/uChat.pas Tue Dec 31 15:19:43 2024 +0100 @@ -108,7 +108,7 @@ procedure UpdateCursorCoords(); forward; // relevant for UTF-8 handling -function IsFirstCharByte(c: char): boolean; inline; +function IsFirstCharByte(c: char): boolean; begin // based on https://en.wikipedia.org/wiki/UTF-8#Description IsFirstCharByte:= (byte(c) and $C0) <> $80; diff -r 2b4f361e3891 -r 629d5123a979 hedgewars/uCollisions.pas --- a/hedgewars/uCollisions.pas Sat Sep 28 22:27:13 2024 +0200 +++ b/hedgewars/uCollisions.pas Tue Dec 31 15:19:43 2024 +0100 @@ -70,11 +70,11 @@ function CheckGearsLineCollision(Gear: PGear; oX, oY, tX, tY: hwFloat): PGearArray; function CheckAllGearsLineCollision(SourceGear: PGear; oX, oY, tX, tY: hwFloat): PGearArray; -function UpdateHitOrder(Gear: PGear; Order: LongInt): boolean; inline; -function UpdateHitOrder(Gear: PGear; Order: LongInt; Global: boolean): boolean; inline; -function UpdateGlobalHitOrder(Gear: PGear; Order: LongInt): boolean; inline; -procedure ClearHitOrderLeq(MinOrder: LongInt); inline; -procedure ClearGlobalHitOrderLeq(MinOrder: LongInt); inline; +function UpdateHitOrder(Gear: PGear; Order: LongInt): boolean; +function UpdateHitOrder(Gear: PGear; Order: LongInt; Global: boolean): boolean; +function UpdateGlobalHitOrder(Gear: PGear; Order: LongInt): boolean; +procedure ClearHitOrderLeq(MinOrder: LongInt); +procedure ClearGlobalHitOrderLeq(MinOrder: LongInt); procedure ClearHitOrder(); procedure RefillProximityCache(SourceGear: PGear; radius: LongInt); @@ -84,16 +84,16 @@ function TestCollisionXImpl(centerX, centerY, radius, direction: LongInt; collisionMask: Word): Word; function TestCollisionYImpl(centerX, centerY, radius, direction: LongInt; collisionMask: Word): Word; -function TestCollisionXwithGear(Gear: PGear; Dir: LongInt): Word; inline; -function TestCollisionYwithGear(Gear: PGear; Dir: LongInt): Word; inline; +function TestCollisionXwithGear(Gear: PGear; Dir: LongInt): Word; +function TestCollisionYwithGear(Gear: PGear; Dir: LongInt): Word; -function TestCollisionX(Gear: PGear; Dir: LongInt): Word; inline; -function TestCollisionY(Gear: PGear; Dir: LongInt): Word; inline; +function TestCollisionX(Gear: PGear; Dir: LongInt): Word; +function TestCollisionY(Gear: PGear; Dir: LongInt): Word; -function TestCollisionXwithXYShift(Gear: PGear; ShiftX: hwFloat; ShiftY: LongInt; Dir: LongInt): Word; inline; -function TestCollisionXwithXYShift(Gear: PGear; ShiftX: hwFloat; ShiftY: LongInt; Dir: LongInt; withGear: boolean): Word; inline; -function TestCollisionYwithXYShift(Gear: PGear; ShiftX, ShiftY: LongInt; Dir: LongInt): Word; inline; -function TestCollisionYwithXYShift(Gear: PGear; ShiftX, ShiftY: LongInt; Dir: LongInt; withGear: boolean): Word; inline; +function TestCollisionXwithXYShift(Gear: PGear; ShiftX: hwFloat; ShiftY: LongInt; Dir: LongInt): Word; +function TestCollisionXwithXYShift(Gear: PGear; ShiftX: hwFloat; ShiftY: LongInt; Dir: LongInt; withGear: boolean): Word; +function TestCollisionYwithXYShift(Gear: PGear; ShiftX, ShiftY: LongInt; Dir: LongInt): Word; +function TestCollisionYwithXYShift(Gear: PGear; ShiftX, ShiftY: LongInt; Dir: LongInt; withGear: boolean): Word; function TestCollisionXKickImpl(centerX, centerY, radius, direction: LongInt; collisionMask, kickMask: Word): TKickTest; function TestCollisionYKickImpl(centerX, centerY, radius, direction: LongInt; collisionMask, kickMask: Word): TKickTest; @@ -103,7 +103,7 @@ function TestRectangleForObstacle(x1, y1, x2, y2: LongInt; landOnly: boolean): boolean; -function CheckCoordInWater(X, Y: LongInt): boolean; inline; +function CheckCoordInWater(X, Y: LongInt): boolean; // returns: negative sign if going downhill to left, value is steepness (noslope/error = _0, 45 = _0_5) function CalcSlopeBelowGear(Gear: PGear): hwFloat; @@ -113,7 +113,7 @@ function CheckGearsUnderSprite(Sprite: TSprite; sprX, sprY, Frame: LongInt): boolean; implementation -uses uConsts, uLandGraphics, uVariables, SDLh, uLandTexture, uDebug; +uses uConsts, uLandGraphics, uVariables, SDLh, uLandTexture, uDebug, uLandUtils; type TCollisionEntry = record X, Y, Radius: LongInt; @@ -159,7 +159,7 @@ end; end; -function CheckCoordInWater(X, Y: LongInt): boolean; inline; +function CheckCoordInWater(X, Y: LongInt): boolean; begin CheckCoordInWater:= (Y > cWaterLine) or ((WorldEdge = weSea) and ((X < leftX) or (X > rightX))); @@ -221,7 +221,7 @@ function LineCollisionTest(oX, oY, dirX, dirY, dirNormSqr, dirNormBound: hwFloat; width: LongInt; Gear: PGear): - TLineCollision; inline; + TLineCollision; var toCenterX, toCenterY, r, b, bSqr, c, desc, t: hwFloat; realT: extended; @@ -367,12 +367,12 @@ end end; -function UpdateHitOrder(Gear: PGear; Order: LongInt): boolean; inline; +function UpdateHitOrder(Gear: PGear; Order: LongInt): boolean; begin UpdateHitOrder := UpdateHitOrderImpl(@ordera, Gear, Order); end; -function UpdateHitOrder(Gear: PGear; Order: LongInt; Global: boolean): boolean; inline; +function UpdateHitOrder(Gear: PGear; Order: LongInt; Global: boolean): boolean; begin if Global then UpdateHitOrder := UpdateHitOrderImpl(@globalordera, Gear, Order) @@ -380,7 +380,7 @@ UpdateHitOrder := UpdateHitOrderImpl(@ordera, Gear, Order) end; -function UpdateGlobalHitOrder(Gear: PGear; Order: LongInt): boolean; inline; +function UpdateGlobalHitOrder(Gear: PGear; Order: LongInt): boolean; begin UpdateGlobalHitOrder := UpdateHitOrderImpl(@globalordera, Gear, Order); end; @@ -408,12 +408,12 @@ end end; -procedure ClearHitOrderLeq(MinOrder: LongInt); inline; +procedure ClearHitOrderLeq(MinOrder: LongInt); begin ClearHitOrderLeqImpl(@ordera, MinOrder); end; -procedure ClearGlobalHitOrderLeq(MinOrder: LongInt); inline; +procedure ClearGlobalHitOrderLeq(MinOrder: LongInt); begin ClearHitOrderLeqImpl(@globalordera, MinOrder); end; @@ -480,8 +480,8 @@ minY := max(centerY - radius + 1, 0); maxY := min(centerY + radius - 1, LAND_HEIGHT - 1); for y := minY to maxY do - if Land[y, x] and collisionMask <> 0 then - exit(Land[y, x] and collisionMask); + if LandGet(y, x) and collisionMask <> 0 then + exit(LandGet(y, x) and collisionMask); end; TestCollisionXImpl := 0; end; @@ -499,18 +499,18 @@ minX := max(centerX - radius + 1, 0); maxX := min(centerX + radius - 1, LAND_WIDTH - 1); for x := minX to maxX do - if Land[y, x] and collisionMask <> 0 then - exit(Land[y, x] and collisionMask); + if LandGet(y, x) and collisionMask <> 0 then + exit(LandGet(y, x) and collisionMask); end; TestCollisionYImpl := 0; end; -function TestCollisionX(Gear: PGear; Dir: LongInt): Word; inline; +function TestCollisionX(Gear: PGear; Dir: LongInt): Word; begin TestCollisionX := TestCollisionXImpl(hwRound(Gear^.X), hwRound(Gear^.Y), Gear^.Radius, Dir, Gear^.CollisionMask and lfLandMask); end; -function TestCollisionY(Gear: PGear; Dir: LongInt): Word; inline; +function TestCollisionY(Gear: PGear; Dir: LongInt): Word; begin TestCollisionY := TestCollisionYImpl(hwRound(Gear^.X), hwRound(Gear^.Y), Gear^.Radius, Dir, Gear^.CollisionMask and lfLandMask); end; @@ -533,19 +533,19 @@ Gear^.CollisionMask:= lfAll; end; -function TestCollisionXwithGear(Gear: PGear; Dir: LongInt): Word; inline; +function TestCollisionXwithGear(Gear: PGear; Dir: LongInt): Word; begin LegacyFixupX(Gear); TestCollisionXwithGear:= TestCollisionXImpl(hwRound(Gear^.X), hwRound(Gear^.Y), Gear^.Radius, Dir, Gear^.CollisionMask); end; -function TestCollisionYwithGear(Gear: PGear; Dir: LongInt): Word; inline; +function TestCollisionYwithGear(Gear: PGear; Dir: LongInt): Word; begin LegacyFixupY(Gear); TestCollisionYwithGear:= TestCollisionYImpl(hwRound(Gear^.X), hwRound(Gear^.Y), Gear^.Radius, Dir, Gear^.CollisionMask); end; -function TestCollisionXwithXYShift(Gear: PGear; ShiftX: hwFloat; ShiftY: LongInt; Dir: LongInt; withGear: boolean): Word; inline; +function TestCollisionXwithXYShift(Gear: PGear; ShiftX: hwFloat; ShiftY: LongInt; Dir: LongInt; withGear: boolean): Word; var collisionMask: Word; begin if withGear then @@ -559,7 +559,7 @@ TestCollisionXwithXYShift := TestCollisionXImpl(hwRound(Gear^.X + ShiftX), hwRound(Gear^.Y) + ShiftY, Gear^.Radius, Dir, collisionMask) end; -function TestCollisionYwithXYShift(Gear: PGear; ShiftX, ShiftY: LongInt; Dir: LongInt; withGear: boolean): Word; inline; +function TestCollisionYwithXYShift(Gear: PGear; ShiftX, ShiftY: LongInt; Dir: LongInt; withGear: boolean): Word; var collisionMask: Word; begin if withGear then @@ -573,12 +573,12 @@ TestCollisionYwithXYShift := TestCollisionYImpl(hwRound(Gear^.X) + ShiftX, hwRound(Gear^.Y) + ShiftY, Gear^.Radius, Dir, collisionMask) end; -function TestCollisionXwithXYShift(Gear: PGear; ShiftX: hwFloat; ShiftY: LongInt; Dir: LongInt): Word; inline; +function TestCollisionXwithXYShift(Gear: PGear; ShiftX: hwFloat; ShiftY: LongInt; Dir: LongInt): Word; begin TestCollisionXwithXYShift:= TestCollisionXwithXYShift(Gear, ShiftX, ShiftY, Dir, true); end; -function TestCollisionYwithXYShift(Gear: PGear; ShiftX, ShiftY: LongInt; Dir: LongInt): Word; inline; +function TestCollisionYwithXYShift(Gear: PGear; ShiftX, ShiftY: LongInt; Dir: LongInt): Word; begin TestCollisionYwithXYShift:= TestCollisionYwithXYShift(Gear, ShiftX, ShiftY, Dir, true); end; @@ -599,16 +599,16 @@ minY := max(centerY - radius + 1, 0); maxY := min(centerY + radius - 1, LAND_HEIGHT - 1); for y := minY to maxY do - if Land[y, x] and collisionMask <> 0 then + if LandGet(y, x) and collisionMask <> 0 then begin TestCollisionXKickImpl.kick := false; - TestCollisionXKickImpl.collisionMask := Land[y, x] and collisionMask; + TestCollisionXKickImpl.collisionMask := LandGet(y, x) and collisionMask; exit end - else if Land[y, x] and kickMask <> 0 then + else if LandGet(y, x) and kickMask <> 0 then begin TestCollisionXKickImpl.kick := true; - TestCollisionXKickImpl.collisionMask := Land[y, x] and kickMask; + TestCollisionXKickImpl.collisionMask := LandGet(y, x) and kickMask; end; end; end; @@ -629,16 +629,16 @@ minX := max(centerX - radius + 1, 0); maxX := min(centerX + radius - 1, LAND_WIDTH - 1); for x := minX to maxX do - if Land[y, x] and collisionMask <> 0 then + if LandGet(y, x) and collisionMask <> 0 then begin TestCollisionYKickImpl.kick := false; - TestCollisionYKickImpl.collisionMask := Land[y, x] and collisionMask; + TestCollisionYKickImpl.collisionMask := LandGet(y, x) and collisionMask; exit end - else if Land[y, x] and kickMask <> 0 then + else if LandGet(y, x) and kickMask <> 0 then begin TestCollisionYKickImpl.kick := true; - TestCollisionYKickImpl.collisionMask := Land[y, x] and kickMask; + TestCollisionYKickImpl.collisionMask := LandGet(y, x) and kickMask; end; end; end; @@ -767,7 +767,7 @@ for y := y1 to y2 do for x := x1 to x2 do - if ((y and LAND_HEIGHT_MASK) = 0) and ((x and LAND_WIDTH_MASK) = 0) and (Land[y, x] > TestWord) then + if ((y and LAND_HEIGHT_MASK) = 0) and ((x and LAND_WIDTH_MASK) = 0) and (LandGet(y, x) > TestWord) then exit; TestRectangleForObstacle:= false @@ -816,7 +816,7 @@ tmpy:= collisionY + k * my; if (((tmpy) and LAND_HEIGHT_MASK) = 0) and (((tmpx) and LAND_WIDTH_MASK) = 0) then - if (Land[tmpy,tmpx] > TestWord) then + if (LandGet(tmpy,tmpx) > TestWord) then begin // remember the index belonging to the first and last collision (if in 1st half) if (i <> 0) then @@ -867,7 +867,7 @@ tmpx:= ldx + k * offset[tmpo,0]; tmpy:= ldy + k * offset[tmpo,1]; if (((tmpy) and LAND_HEIGHT_MASK) = 0) and (((tmpx) and LAND_WIDTH_MASK) = 0) - and (Land[tmpy,tmpx] > TestWord) then + and (LandGet(tmpy,tmpx) > TestWord) then begin ldx:= tmpx; ldy:= tmpy; @@ -891,7 +891,7 @@ tmpx:= rdx + k * offset[tmpo,0]; tmpy:= rdy + k * offset[tmpo,1]; if (((tmpy) and LAND_HEIGHT_MASK) = 0) and (((tmpx) and LAND_WIDTH_MASK) = 0) - and (Land[tmpy,tmpx] > TestWord) then + and (LandGet(tmpy,tmpx) > TestWord) then begin rdx:= tmpx; rdy:= tmpy; @@ -934,7 +934,7 @@ i:= x + Gear^.Radius * 2 - 2; repeat if (x and LAND_WIDTH_MASK) = 0 then - if Land[y, x] <> 0 then + if LandGet(y, x) <> 0 then if (not isColl) or (abs(x-gx) < abs(collX-gx)) then begin isColl:= true; @@ -957,7 +957,7 @@ i:= y + Gear^.Radius * 2 - 2; repeat if (y and LAND_HEIGHT_MASK) = 0 then - if Land[y, x] <> 0 then + if LandGet(y, x) <> 0 then if (not isColl) or (abs(y-gy) < abs(collY-gy)) then begin isColl:= true; @@ -1026,7 +1026,7 @@ i:= x + Gear^.Radius * 2 - 2; repeat if (x and LAND_WIDTH_MASK) = 0 then - if (Land[y, x] and lfLandMask) <> 0 then + if (LandGet(y, x) and lfLandMask) <> 0 then if (not isColl) or (abs(x-gx) < abs(collX-gx)) then begin isColl:= true; diff -r 2b4f361e3891 -r 629d5123a979 hedgewars/uCommandHandlers.pas --- a/hedgewars/uCommandHandlers.pas Sat Sep 28 22:27:13 2024 +0200 +++ b/hedgewars/uCommandHandlers.pas Tue Dec 31 15:19:43 2024 +0100 @@ -587,7 +587,7 @@ if bShowAmmoMenu then bShowAmmoMenu:= false - else if not(CurrentTeam^.Extdriven) and ((Gear = nil) or ((Gear^.State and (gstAttacking or gstAttacked)) <> 0) + else if ((Gear = nil) or ((Gear^.State and (gstAttacking or gstAttacked)) <> 0) or ((Gear^.State and gstHHDriven) = 0)) then begin end diff -r 2b4f361e3891 -r 629d5123a979 hedgewars/uCommands.pas --- a/hedgewars/uCommands.pas Sat Sep 28 22:27:13 2024 +0200 +++ b/hedgewars/uCommands.pas Tue Dec 31 15:19:43 2024 +0100 @@ -30,7 +30,7 @@ procedure freeModule; procedure RegisterVariable(Name: shortstring; p: TCommandHandler; Trusted: boolean; Rand: boolean); procedure RegisterVariable(Name: shortstring; p: TCommandHandler; Trusted: boolean); -procedure ParseCommand(CmdStr: shortstring; TrustedSource: boolean); inline; +procedure ParseCommand(CmdStr: shortstring; TrustedSource: boolean); procedure ParseCommand(CmdStr: shortstring; TrustedSource, ExternalSource: boolean); procedure ParseTeamCommand(s: shortstring); procedure StopMessages(Message: Longword); @@ -76,7 +76,7 @@ end; -procedure ParseCommand(CmdStr: shortstring; TrustedSource: boolean); inline; +procedure ParseCommand(CmdStr: shortstring; TrustedSource: boolean); begin ParseCommand(CmdStr, TrustedSource, false) end; diff -r 2b4f361e3891 -r 629d5123a979 hedgewars/uDebug.pas --- a/hedgewars/uDebug.pas Sat Sep 28 22:27:13 2024 +0200 +++ b/hedgewars/uDebug.pas Tue Dec 31 15:19:43 2024 +0100 @@ -23,7 +23,7 @@ interface procedure OutError(Msg: shortstring; isFatalError: boolean); -//procedure TryDo(Assert: boolean; Msg: shortstring; isFatal: boolean); inline; +//procedure TryDo(Assert: boolean; Msg: shortstring; isFatal: boolean); function checkFails(Assert: boolean; Msg: shortstring; isFatal: boolean): boolean; function SDLCheck(Assert: boolean; Msg: shortstring; isFatal: boolean): boolean; diff -r 2b4f361e3891 -r 629d5123a979 hedgewars/uFloat.pas --- a/hedgewars/uFloat.pas Sat Sep 28 22:27:13 2024 +0200 +++ b/hedgewars/uFloat.pas Tue Dec 31 15:19:43 2024 +0100 @@ -56,45 +56,45 @@ {$ENDIF} // Returns an hwFloat that represents the value of integer parameter i -function int2hwFloat (const i: LongInt) : hwFloat; inline; -function hwFloat2Float (const i: hwFloat) : extended; inline; +function int2hwFloat (const i: LongInt) : hwFloat; +function hwFloat2Float (const i: hwFloat) : extended; // The implemented operators -operator = (const z1, z2: hwFloat) z : boolean; inline; +operator = (const z1, z2: hwFloat) z : boolean; {$IFDEF PAS2C} -operator <> (const z1, z2: hwFloat) z : boolean; inline; +operator <> (const z1, z2: hwFloat) z : boolean; {$ENDIF} -operator + (const z1, z2: hwFloat) z : hwFloat; inline; -operator - (const z1, z2: hwFloat) z : hwFloat; inline; -operator - (const z1: hwFloat) z : hwFloat; inline; +operator + (const z1, z2: hwFloat) z : hwFloat; +operator - (const z1, z2: hwFloat) z : hwFloat; +operator - (const z1: hwFloat) z : hwFloat; -operator * (const z1, z2: hwFloat) z : hwFloat; inline; -operator * (const z1: hwFloat; const z2: LongInt) z : hwFloat; inline; -operator / (const z1: hwFloat; z2: hwFloat) z : hwFloat; inline; -operator / (const z1: hwFloat; const z2: LongInt) z : hwFloat; inline; +operator * (const z1, z2: hwFloat) z : hwFloat; +operator * (const z1: hwFloat; const z2: LongInt) z : hwFloat; +operator / (const z1: hwFloat; z2: hwFloat) z : hwFloat; +operator / (const z1: hwFloat; const z2: LongInt) z : hwFloat; -operator < (const z1, z2: hwFloat) b : boolean; inline; -operator > (const z1, z2: hwFloat) b : boolean; inline; +operator < (const z1, z2: hwFloat) b : boolean; +operator > (const z1, z2: hwFloat) b : boolean; // Various functions for hwFloat (some are inlined in the resulting code for better performance) function cstr(const z: hwFloat): shortstring; // Returns a shortstring representations of the hwFloat. -function hwRound(const t: hwFloat): LongInt; inline; // Does NOT really round but returns the integer representation of the hwFloat without fractional digits. (-_0_9 -> -0, _1_5 -> _1) -function hwAbs(const t: hwFloat): hwFloat; inline; // Returns the value of t with positive sign. -function hwSqr(const t: hwFloat): hwFloat; inline; // Returns the square value of parameter t. -function hwSqrt1(const t: hwFloat): hwFloat; inline; // Returns the the positive square root of parameter t. -function hwSqrt(const x: hwFloat): hwFloat; inline; // Returns the the positive square root of parameter t. +function hwRound(const t: hwFloat): LongInt; // Does NOT really round but returns the integer representation of the hwFloat without fractional digits. (-_0_9 -> -0, _1_5 -> _1) +function hwAbs(const t: hwFloat): hwFloat; // Returns the value of t with positive sign. +function hwSqr(const t: hwFloat): hwFloat; // Returns the square value of parameter t. +function hwSqrt1(const t: hwFloat): hwFloat; // Returns the the positive square root of parameter t. +function hwSqrt(const x: hwFloat): hwFloat; // Returns the the positive square root of parameter t. function Distance(const dx, dy: hwFloat): hwFloat; // Returns the distance between two points in 2-dimensional space, of which the parameters are the horizontal and vertical distance. function DistanceI(const dx, dy: LongInt): hwFloat; // Same as above for integer parameters. function AngleSin(const Angle: Longword): hwFloat; function AngleCos(const Angle: Longword): hwFloat; function vector2Angle(const x, y: hwFloat): LongInt; -function SignAs(const num, signum: hwFloat): hwFloat; inline; // Returns an hwFloat with the value of parameter num and the sign of signum. -function hwSign(r: hwFloat): LongInt; inline; // Returns an integer with value 1 and sign of parameter r. -function hwSignf(r: real): LongInt; inline; // Returns an integer with value 1 and sign of parameter r. -function isZero(const z: hwFloat): boolean; inline; +function SignAs(const num, signum: hwFloat): hwFloat; // Returns an hwFloat with the value of parameter num and the sign of signum. +function hwSign(r: hwFloat): LongInt; // Returns an integer with value 1 and sign of parameter r. +function hwSignf(r: real): LongInt; // Returns an integer with value 1 and sign of parameter r. +function isZero(const z: hwFloat): boolean; {$WARNINGS OFF} // some hwFloat constants @@ -181,6 +181,7 @@ _300: hwFloat = (isNegative: false; QWordValue: 4294967296 * 300); _360: hwFloat = (isNegative: false; QWordValue: 4294967296 * 360); _450: hwFloat = (isNegative: false; QWordValue: 4294967296 * 450); + _900: hwFloat = (isNegative: false; QWordValue: 4294967296 * 900); _1000: hwFloat = (isNegative: false; QWordValue: 4294967296 * 1000); _1024: hwFloat = (isNegative: false; QWordValue: 4294967296 * 1024); _2048: hwFloat = (isNegative: false; QWordValue: 4294967296 * 2048); @@ -195,33 +196,33 @@ uses uSinTable; -function int2hwFloat (const i: LongInt) : hwFloat; inline; +function int2hwFloat (const i: LongInt) : hwFloat; begin int2hwFloat.isNegative:= i < 0; int2hwFloat.Round:= abs(i); int2hwFloat.Frac:= 0 end; -function hwFloat2Float (const i: hwFloat) : extended; inline; +function hwFloat2Float (const i: hwFloat) : extended; begin hwFloat2Float:= i.Frac / $100000000 + i.Round; if i.isNegative then hwFloat2Float:= -hwFloat2Float; end; -operator = (const z1, z2: hwFloat) z : boolean; inline; +operator = (const z1, z2: hwFloat) z : boolean; begin z:= (z1.isNegative = z2.isNegative) and (z1.QWordValue = z2.QWordValue); end; {$IFDEF PAS2C} -operator <> (const z1, z2: hwFloat) z : boolean; inline; +operator <> (const z1, z2: hwFloat) z : boolean; begin z:= (z1.isNegative <> z2.isNegative) or (z1.QWordValue <> z2.QWordValue); end; {$ENDIF} -operator + (const z1, z2: hwFloat) z : hwFloat; inline; +operator + (const z1, z2: hwFloat) z : hwFloat; begin if z1.isNegative = z2.isNegative then begin @@ -241,7 +242,7 @@ end end; -operator - (const z1, z2: hwFloat) z : hwFloat; inline; +operator - (const z1, z2: hwFloat) z : hwFloat; begin if z1.isNegative = z2.isNegative then if z1.QWordValue > z2.QWordValue then @@ -261,12 +262,12 @@ end end; -function isZero(const z: hwFloat): boolean; inline; +function isZero(const z: hwFloat): boolean; begin isZero := z.QWordValue = 0; end; -operator < (const z1, z2: hwFloat) b : boolean; inline; +operator < (const z1, z2: hwFloat) b : boolean; begin if z1.isNegative xor z2.isNegative then b:= z1.isNegative @@ -277,7 +278,7 @@ b:= (z2.QWordValue < z1.QWordValue) = z1.isNegative end; -operator > (const z1, z2: hwFloat) b : boolean; inline; +operator > (const z1, z2: hwFloat) b : boolean; begin if z1.isNegative xor z2.isNegative then b:= z2.isNegative @@ -288,14 +289,14 @@ b:= (z1.QWordValue > z2.QWordValue) <> z2.isNegative end; -operator - (const z1: hwFloat) z : hwFloat; inline; +operator - (const z1: hwFloat) z : hwFloat; begin z:= z1; z.isNegative:= not z.isNegative end; -operator * (const z1, z2: hwFloat) z : hwFloat; inline; +operator * (const z1, z2: hwFloat) z : hwFloat; begin z.isNegative:= z1.isNegative xor z2.isNegative; @@ -310,13 +311,13 @@ end end; -operator * (const z1: hwFloat; const z2: LongInt) z : hwFloat; inline; +operator * (const z1: hwFloat; const z2: LongInt) z : hwFloat; begin z.isNegative:= z1.isNegative xor (z2 < 0); z.QWordValue:= z1.QWordValue * abs(z2) end; -operator / (const z1: hwFloat; z2: hwFloat) z : hwFloat; inline; +operator / (const z1: hwFloat; z2: hwFloat) z : hwFloat; var t: QWord; begin z.isNegative:= z1.isNegative xor z2.isNegative; @@ -337,7 +338,7 @@ end end; -operator / (const z1: hwFloat; const z2: LongInt) z : hwFloat; inline; +operator / (const z1: hwFloat; const z2: LongInt) z : hwFloat; begin z.isNegative:= z1.isNegative xor (z2 < 0); z.QWordValue:= z1.QWordValue div abs(z2) @@ -371,7 +372,7 @@ hwAbs.isNegative:= false end; -function hwSqr(const t: hwFloat): hwFloat; inline; +function hwSqr(const t: hwFloat): hwFloat; begin hwSqr.isNegative:= false; hwSqr.QWordValue:= ((QWord(t.Round) * t.Round) shl 32) + QWord(t.Round) * t.Frac * 2 + ((QWord(t.Frac) * t.Frac) shr 32); diff -r 2b4f361e3891 -r 629d5123a979 hedgewars/uGearsHandlers.pas --- a/hedgewars/uGearsHandlers.pas Sat Sep 28 22:27:13 2024 +0200 +++ b/hedgewars/uGearsHandlers.pas Tue Dec 31 15:19:43 2024 +0100 @@ -36,13 +36,13 @@ (x: 0; y: 1), (x: -1; y: 0)); -procedure PrevAngle(Gear: PGear; dA: LongInt); inline; +procedure PrevAngle(Gear: PGear; dA: LongInt); begin inc(Gear^.WDTimer); Gear^.Angle := (LongInt(Gear^.Angle) - dA) and 3 end; -procedure NextAngle(Gear: PGear; dA: LongInt); inline; +procedure NextAngle(Gear: PGear; dA: LongInt); begin inc(Gear^.WDTimer); Gear^.Angle := (LongInt(Gear^.Angle) + dA) and 3 diff -r 2b4f361e3891 -r 629d5123a979 hedgewars/uGearsHandlersMess.pas --- a/hedgewars/uGearsHandlersMess.pas Sat Sep 28 22:27:13 2024 +0200 +++ b/hedgewars/uGearsHandlersMess.pas Tue Dec 31 15:19:43 2024 +0100 @@ -152,7 +152,7 @@ uses uConsts, uVariables, uVisualGearsList, uRandom, uCollisions, uGearsList, uUtils, uSound , SDLh, uScript, uGearsHedgehog, uGearsUtils, uIO, uCaptions, uLandGraphics , uGearsHandlers, uTextures, uRenderUtils, uAmmos, uTeams, uLandTexture - , uStore, uAI, uStats, uLocale; + , uStore, uAI, uStats, uLocale, uLandUtils; procedure doStepPerPixel(Gear: PGear; step: TGearStepProcedure; onlyCheckIfChanged: boolean); var @@ -528,7 +528,7 @@ end else if (collV < 0) and (collH < 0) and tdX.isNegative and tdY.isNegative then Gear^.dX.isNegative := false; - + isFalling := false; Gear^.AdvBounce := 10; end; @@ -884,45 +884,45 @@ else if (cGravity.isNegative) and (yy < LAND_HEIGHT-1200) then move:=true // Solid pixel encountered - else if ((yy and LAND_HEIGHT_MASK) = 0) and ((xx and LAND_WIDTH_MASK) = 0) and (Land[yy, xx] <> 0) then - begin - lf:= Land[yy, xx] and (lfObject or lfBasic or lfIndestructible); + else if ((yy and LAND_HEIGHT_MASK) = 0) and ((xx and LAND_WIDTH_MASK) = 0) and (LandGet(yy, xx) <> 0) then + begin + lf:= LandGet(yy, xx) and (lfObject or lfBasic or lfIndestructible); if lf = 0 then lf:= lfObject; // If there's room below keep falling - if (((yy-1) and LAND_HEIGHT_MASK) = 0) and (Land[yy-1, xx] = 0) then + if (((yy-1) and LAND_HEIGHT_MASK) = 0) and (LandGet(yy-1, xx) = 0) then begin X:= X - cWindSpeed * 1600 - dX; end // If there's room below, on the sides, fill the gaps - else if (((yy-1) and LAND_HEIGHT_MASK) = 0) then + else if (((yy-1) and LAND_HEIGHT_MASK) = 0) then begin - if (((xx - 1) and LAND_WIDTH_MASK) = 0) and (Land[yy - 1, (xx - 1)] = 0) then + if (((xx - 1) and LAND_WIDTH_MASK) = 0) and (LandGet(yy - 1, (xx - 1)) = 0) then begin X:= X - _0_8; Y:= oldY; end - else if (((xx - 2) and LAND_WIDTH_MASK) = 0) and (Land[yy - 1, (xx - 2)] = 0) then + else if (((xx - 2) and LAND_WIDTH_MASK) = 0) and (LandGet(yy - 1, (xx - 2)) = 0) then begin X:= X - _1_6; Y:= oldY; end - else if (((xx + 1) and LAND_WIDTH_MASK) = 0) and (Land[yy - 1, (xx + 1)] = 0) then + else if (((xx + 1) and LAND_WIDTH_MASK) = 0) and (LandGet(yy - 1, (xx + 1)) = 0) then begin X:= X + _0_8; Y:= oldY; end - else if (((xx + 2) and LAND_WIDTH_MASK) = 0) and (Land[yy - 1, (xx + 2)] = 0) then + else if (((xx + 2) and LAND_WIDTH_MASK) = 0) and (LandGet(yy - 1, (xx + 2)) = 0) then begin X:= X + _1_6; Y:= oldY; end else - if ((((yy+1) and LAND_HEIGHT_MASK) = 0) and ((Land[yy + 1, xx] and $FF) <> 0)) then - move:=true - else + if ((((yy+1) and LAND_HEIGHT_MASK) = 0) and ((LandGet(yy + 1, xx) and $FF) <> 0)) then + move:=true + else draw:= true end // if there's an hog/object below do nothing - else if ((((yy+1) and LAND_HEIGHT_MASK) = 0) and ((Land[yy+1, xx] and $FF) <> 0)) + else if ((((yy+1) and LAND_HEIGHT_MASK) = 0) and ((LandGet(yy+1, xx) and $FF) <> 0)) then move:=true else draw:= true end @@ -949,7 +949,7 @@ for px:= 0 to Pred(s^.w) do begin lx:=xx + px; ly:=yy + py; - if (ly and LAND_HEIGHT_MASK = 0) and (lx and LAND_WIDTH_MASK = 0) and (Land[ly, lx] and $FF = 0) then + if (ly and LAND_HEIGHT_MASK = 0) and (lx and LAND_WIDTH_MASK = 0) and (LandGet(ly, lx) and $FF = 0) then begin rx:= lx; ry:= ly; @@ -957,21 +957,21 @@ begin rx:= rx div 2;ry:= ry div 2; end; - if Land[yy + py, xx + px] <= lfAllObjMask then + if LandGet(yy + py, xx + px) <= lfAllObjMask then if gun then begin LandDirty[yy div 32, xx div 32]:= 1; - if LandPixels[ry, rx] = 0 then - Land[ly, lx]:= lfDamaged or lfObject - else Land[ly, lx]:= lfDamaged or lfBasic + if LandPixelGet(ry, rx) = 0 then + LandSet(ly, lx, lfDamaged or lfObject) + else LandSet(ly, lx, lfDamaged or lfBasic) end - else Land[ly, lx]:= lf; + else LandSet(ly, lx, lf); if gun then - LandPixels[ry, rx]:= (Gear^.Tint shr 24 shl RShift) or - (Gear^.Tint shr 16 and $FF shl GShift) or - (Gear^.Tint shr 8 and $FF shl BShift) or - (p^[px] and AMask) - else LandPixels[ry, rx]:= addBgColor(LandPixels[ry, rx], p^[px]); + LandPixelSet(ry, rx, (Gear^.Tint shr 24 shl RShift) or + (Gear^.Tint shr 16 and $FF shl GShift) or + (Gear^.Tint shr 8 and $FF shl BShift) or + (p^[px] and AMask)) + else LandPixelSet(ry, rx, addBgColor(LandPixelGet(ry, rx), p^[px])); end else allpx:= false end; @@ -1520,7 +1520,7 @@ if ((y and LAND_HEIGHT_MASK) = 0) and ((x and LAND_WIDTH_MASK) = 0) then begin - LandFlags:= Land[y, x]; + LandFlags:= LandGet(y, x); if LandFlags <> 0 then inc(Gear^.Damage); isDigging:= (LandFlags and lfLandMask) <> 0; end; @@ -1762,7 +1762,7 @@ if (Gear^.Timer mod 47) = 0 then begin // ok. this was an attempt to turn off dust if not actually drilling land. I have no idea why it isn't working as expected - if (( (y + 12) and LAND_HEIGHT_MASK) = 0) and ((x and LAND_WIDTH_MASK) = 0) and (Land[y + 12, x] > 255) then + if (( (y + 12) and LAND_HEIGHT_MASK) = 0) and ((x and LAND_WIDTH_MASK) = 0) and (LandGet(y + 12, x) > 255) then for i:= 0 to 1 do AddVisualGear(x - 5 + Random(10), y + 12, vgtDust); @@ -2194,7 +2194,7 @@ // If in ready timer, or after turn, or in first 5 seconds of turn (really a window due to extra time utility) // or hunting is disabled due to seek radius of 0 then we aren't hunting - if (ReadyTimeLeft > 0) or (TurnTimeLeft = 0) or + if (ReadyTimeLeft > 0) or (TurnTimeLeft = 0) or ((TurnTimeLeft < cHedgehogTurnTime) and (cHedgehogTurnTime-TurnTimeLeft < 5000)) or (Gear^.Angle = 0) then gear^.State:= gear^.State and (not gstChooseTarget) @@ -3452,7 +3452,7 @@ begin DeleteGear(Gear); exit - end; + end; valid:= false; @@ -4246,8 +4246,8 @@ dec(playWidth, 2); for i:= 0 to LAND_HEIGHT - 1 do begin - Land[i, leftX] := 0; - Land[i, rightX] := 0; + LandSet(i, leftX, 0); + LandSet(i, rightX, 0); end; end; @@ -4255,7 +4255,7 @@ begin dec(cWaterLine); for i:= 0 to LAND_WIDTH - 1 do - Land[cWaterLine, i] := 0; + LandSet(cWaterLine, i, 0); SetAllToActive end; @@ -5039,8 +5039,8 @@ doPortalColorSwitch(); // destroy portal if ground it was attached too is gone - if (Land[hwRound(Gear^.Y), hwRound(Gear^.X)] <= lfAllObjMask) - or (Land[hwRound(Gear^.Y), hwRound(Gear^.X)] and lfBouncy <> 0) + if (LandGet(hwRound(Gear^.Y), hwRound(Gear^.X)) <= lfAllObjMask) + or (LandGet(hwRound(Gear^.Y), hwRound(Gear^.X)) and lfBouncy <> 0) or (Gear^.Timer < 1) or (Gear^.Hedgehog^.Team <> CurrentHedgehog^.Team) or CheckCoordInWater(hwRound(Gear^.X), hwRound(Gear^.Y)) then @@ -5407,12 +5407,12 @@ ty := 0; // avoid compiler hints - if ((y and LAND_HEIGHT_MASK) = 0) and ((x and LAND_WIDTH_MASK) = 0) and (Land[y, x] > 255) then + if ((y and LAND_HEIGHT_MASK) = 0) and ((x and LAND_WIDTH_MASK) = 0) and (LandGet(y, x) > 255) then begin Gear^.State := Gear^.State or gstCollision; Gear^.State := Gear^.State and (not gstMoving); - if (Land[y, x] and lfBouncy <> 0) + if (LandGet(y, x) and lfBouncy <> 0) or (not CalcSlopeTangent(Gear, x, y, tx, ty, 255)) or (DistanceI(tx,ty) < _12) then // reject shots at too irregular terrain begin @@ -5773,9 +5773,9 @@ if (not CheckCoordInWater(rX, rY)) or (not CheckCoordInWater(x, y)) then begin if ((y and LAND_HEIGHT_MASK) = 0) and ((x and LAND_WIDTH_MASK) = 0) - and (Land[y, x] <> 0) then + and (LandGet(y, x) <> 0) then begin - if ((GameFlags and gfSolidLand) <> 0) and (Land[y, x] > 255) then + if ((GameFlags and gfSolidLand) <> 0) and (LandGet(y, x) > 255) then Gear^.Damage := initHealth else if justCollided then begin @@ -6683,7 +6683,7 @@ ndY:= -AngleCos(HHGear^.Angle) * _4; if (ndX <> dX) or (ndY <> dY) or (Gear^.Message and (gmUp or gmDown) <> 0) or (((Target.X <> NoPointX) and (Target.X and LAND_WIDTH_MASK = 0) and - (Target.Y and LAND_HEIGHT_MASK = 0) and ((Land[Target.Y, Target.X] = 0)) and + (Target.Y and LAND_HEIGHT_MASK = 0) and ((LandGet(Target.Y, Target.X) = 0)) and (not CheckCoordInWater(Target.X, Target.Y))) and (CheckGearNear(gtAirMine, int2hwFloat(Target.X),int2hwFloat(Target.Y), Gear^.Radius*3, Gear^.Radius*3) = nil) and (not ((WorldEdge = weBounce) and ((Target.X > rightX) or (Target.X < leftX))))) then begin @@ -6717,7 +6717,7 @@ else if CheckCoordInWater(Target.X, Target.Y) or ((Target.X and LAND_WIDTH_MASK = 0) and (Target.Y and LAND_HEIGHT_MASK = 0) and - (Land[Target.Y, Target.X] = lfIce) and + (LandGet(Target.Y, Target.X) = lfIce) and ((Target.Y+iceHeight+5 > cWaterLine) or ((WorldEdge = weSea) and ((Target.X+iceHeight+5 > rightX) or @@ -6799,13 +6799,13 @@ begin iter^.Damage:= 0; iter^.State:= iter^.State or gstFrozen; - if (hwRound(iter^.X) < RightX-16) and (hwRound(iter^.X) > LeftX+16) and + if (hwRound(iter^.X) < RightX-16) and (hwRound(iter^.X) > LeftX+16) and (hwRound(iter^.Y) > topY+16) and (hwRound(iter^.Y) < LAND_HEIGHT-16) then begin AddCI(iter); iter^.X:= int2hwFloat(min(RightX-16,max(hwRound(iter^.X), LeftX+16))); iter^.Y:= int2hwFloat(min(LAND_HEIGHT-16,max(hwRound(iter^.Y),TopY+16))); - ForcePlaceOnLand(hwRound(iter^.X)-16, hwRound(iter^.Y)-16, sprFrozenAirMine, 0, lfIce, $FFFFFFFF, false, false, false); + ForcePlaceOnLand(hwRound(iter^.X)-16, hwRound(iter^.Y)-16, sprFrozenAirMine, 0, lfIce, $FFFFFFFF, false, false, false); iter^.State:= iter^.State or gstInvisible end else @@ -6865,7 +6865,7 @@ end else if (t > 400) and (CheckCoordInWater(gX, gY) or (((gX and LAND_WIDTH_MASK = 0) and (gY and LAND_HEIGHT_MASK = 0)) - and (Land[gY, gX] <> 0))) then + and (LandGet(gY, gX) <> 0))) then begin Target.X:= gX; Target.Y:= gY; @@ -6888,7 +6888,7 @@ Target.Y:= gY; X:= HHGear^.X; Y:= HHGear^.Y - end + end end; end end; @@ -6996,7 +6996,7 @@ tX:=Gear^.X-targ^.X; tY:=Gear^.Y-targ^.Y; // allow escaping - should maybe flag this too - if (GameTicks > Gear^.FlightTime+10000) or + if (GameTicks > Gear^.FlightTime+10000) or ((tX.Round+tY.Round > Gear^.Angle*6) and (hwRound(hwSqr(tX) + hwSqr(tY)) > sqr(Gear^.Angle*6))) then targ:= nil @@ -7005,7 +7005,7 @@ // If in ready timer, or after turn, or in first 5 seconds of turn (really a window due to extra time utility) // or mine is inactive due to lack of gsttmpflag or hunting is disabled due to seek radius of 0 // then we aren't hunting - if (ReadyTimeLeft > 0) or (TurnTimeLeft = 0) or + if (ReadyTimeLeft > 0) or (TurnTimeLeft = 0) or ((TurnTimeLeft < cHedgehogTurnTime) and (cHedgehogTurnTime-TurnTimeLeft < 5000)) or (Gear^.State and gsttmpFlag = 0) or (Gear^.Angle = 0) then @@ -7269,7 +7269,7 @@ MakeSentryStep := true end end; - + function MakeSentryJump(Sentry: PGear; maxXStep, maxYStep: LongInt): Boolean; var x, y, offsetX, offsetY, direction: LongInt; jumpTime: hwFloat; @@ -7322,7 +7322,7 @@ for i := 0 to count - 1 do begin - if (Land[hwRound(fromY), hwRound(fromX)] and mask) <> 0 then + if (LandGet(hwRound(fromY), hwRound(fromX)) and mask) <> 0 then Inc(TraceAttackPath); fromX := fromX + distX; fromY := fromY + distY; diff -r 2b4f361e3891 -r 629d5123a979 hedgewars/uGearsHandlersRope.pas --- a/hedgewars/uGearsHandlersRope.pas Sat Sep 28 22:27:13 2024 +0200 +++ b/hedgewars/uGearsHandlersRope.pas Tue Dec 31 15:19:43 2024 +0100 @@ -26,7 +26,7 @@ implementation uses uConsts, uFloat, uCollisions, uVariables, uGearsList, uSound, uGearsUtils, - uAmmos, uDebug, uUtils, uGearsHedgehog, uGearsRender; + uAmmos, uDebug, uUtils, uGearsHedgehog, uGearsRender, uLandUtils; const IsNilHHFatal = false; @@ -241,7 +241,7 @@ begin lx := hwRound(nx); ly := hwRound(ny); - if ((ly and LAND_HEIGHT_MASK) = 0) and ((lx and LAND_WIDTH_MASK) = 0) and (Land[ly, lx] > lfAllObjMask) then + if ((ly and LAND_HEIGHT_MASK) = 0) and ((lx and LAND_WIDTH_MASK) = 0) and (LandGet(ly, lx) > lfAllObjMask) then begin tx := _1 / Distance(ropeDx, ropeDy); // old rope pos @@ -358,7 +358,7 @@ HHGear^.dY := HHGear^.dY * len; end; - haveCollision:= ((hwRound(Gear^.Y) and LAND_HEIGHT_MASK) = 0) and ((hwRound(Gear^.X) and LAND_WIDTH_MASK) = 0) and ((Land[hwRound(Gear^.Y), hwRound(Gear^.X)]) <> 0); + haveCollision:= ((hwRound(Gear^.Y) and LAND_HEIGHT_MASK) = 0) and ((hwRound(Gear^.X) and LAND_WIDTH_MASK) = 0) and ((LandGet(hwRound(Gear^.Y), hwRound(Gear^.X))) <> 0); if not haveCollision then begin @@ -474,7 +474,7 @@ ty := _0; while tt > _20 do begin - if ((hwRound(Gear^.Y+ty) and LAND_HEIGHT_MASK) = 0) and ((hwRound(Gear^.X+tx) and LAND_WIDTH_MASK) = 0) and (Land[hwRound(Gear^.Y+ty), hwRound(Gear^.X+tx)] > lfAllObjMask) then + if ((hwRound(Gear^.Y+ty) and LAND_HEIGHT_MASK) = 0) and ((hwRound(Gear^.X+tx) and LAND_WIDTH_MASK) = 0) and (LandGet(hwRound(Gear^.Y+ty), hwRound(Gear^.X+tx)) > lfAllObjMask) then begin Gear^.X := Gear^.X + tx; Gear^.Y := Gear^.Y + ty; diff -r 2b4f361e3891 -r 629d5123a979 hedgewars/uGearsHedgehog.pas --- a/hedgewars/uGearsHedgehog.pas Sat Sep 28 22:27:13 2024 +0200 +++ b/hedgewars/uGearsHedgehog.pas Tue Dec 31 15:19:43 2024 +0100 @@ -29,7 +29,7 @@ procedure HedgehogChAngle(HHGear: PGear); procedure PickUp(HH, Gear: PGear); procedure AddPickup(HH: THedgehog; ammo: TAmmoType; cnt, X, Y: LongWord); -procedure CheckIce(Gear: PGear); inline; +procedure CheckIce(Gear: PGear); procedure PlayTaunt(taunt: Longword); function HHGetTimer(Gear: PGear): LongWord; function HHGetTimerMsg(Gear: PGear): LongWord; @@ -1529,7 +1529,7 @@ AllInactive:= false end; -procedure CheckIce(Gear: PGear); inline; +procedure CheckIce(Gear: PGear); (* var x,y,tx,ty: LongInt; tdX, tdY, slope: hwFloat; diff -r 2b4f361e3891 -r 629d5123a979 hedgewars/uGearsRender.pas --- a/hedgewars/uGearsRender.pas Sat Sep 28 22:27:13 2024 +0200 +++ b/hedgewars/uGearsRender.pas Tue Dec 31 15:19:43 2024 +0100 @@ -56,7 +56,7 @@ end; implementation -uses uRender, uRenderUtils, uGearsUtils, uUtils, uVariables, uAmmos, Math, uVisualGearsList; +uses uRender, uRenderUtils, uGearsUtils, uUtils, uVariables, uAmmos, Math, uVisualGearsList, uLandUtils; procedure DrawRopeLinesRQ(Gear: PGear); var n: LongInt; @@ -72,7 +72,7 @@ if (RopePoints.Count > 0) or (Gear^.Elasticity.QWordValue > 0) then begin EnableTexture(false); - + Tint(Gear^.Tint shr 24 div 3, Gear^.Tint shr 16 and $FF div 3, Gear^.Tint shr 8 and $FF div 3, Gear^.Tint and $FF); n:= RopePoints.Count + 2; @@ -541,7 +541,7 @@ hy:= ty; wraps:= 0; inWorldBounds := ((ty and LAND_HEIGHT_MASK) or (tx and LAND_WIDTH_MASK)) = 0; - while (inWorldBounds and ((Land[ty, tx] and lfAll) = 0)) or (not inWorldBounds) do + while (inWorldBounds and ((LandGet(ty, tx) and lfAll) = 0)) or (not inWorldBounds) do begin if wraps > cMaxLaserSightWraps then break; @@ -1049,7 +1049,7 @@ ty:= hwRound(Gear^.Y) + cHHRadius + 2; if ((tx and LAND_WIDTH_MASK) = 0) and ((ty and LAND_HEIGHT_MASK) = 0) and - (Land[ty, tx] <> 0) then + (LandGet(ty, tx) <> 0) then AddVisualGear(tx - 2 + Random(4), ty - 8, vgtDust); end; @@ -1417,7 +1417,7 @@ DrawSpriteRotated(sprMineOn, x, y, 0, Gear^.DirAngle) else DrawSpriteRotated(sprMineDead, x, y, 0, Gear^.DirAngle); end; - gtAirMine: + gtAirMine: // render air mine based on its state: // frozen if (Gear^.State and gstFrozen <> 0) then diff -r 2b4f361e3891 -r 629d5123a979 hedgewars/uGearsUtils.pas --- a/hedgewars/uGearsUtils.pas Sat Sep 28 22:27:13 2024 +0200 +++ b/hedgewars/uGearsUtils.pas Tue Dec 31 15:19:43 2024 +0100 @@ -22,7 +22,7 @@ interface uses uTypes, uFloat; -procedure doMakeExplosion(X, Y, Radius: LongInt; AttackingHog: PHedgehog; Mask: Longword); inline; +procedure doMakeExplosion(X, Y, Radius: LongInt; AttackingHog: PHedgehog; Mask: Longword); procedure doMakeExplosion(X, Y, Radius: LongInt; AttackingHog: PHedgehog; Mask: Longword; const Tint: LongWord); procedure AddSplashForGear(Gear: PGear; justSkipping: boolean); procedure AddBounceEffectForGear(Gear: PGear; imageScale: Single); @@ -39,7 +39,7 @@ procedure CalcRotationDirAngle(Gear: PGear); procedure ResurrectHedgehog(var gear: PGear); -procedure FindPlace(var Gear: PGear; withFall: boolean; Left, Right: LongInt); inline; +procedure FindPlace(var Gear: PGear; withFall: boolean; Left, Right: LongInt); procedure FindPlace(var Gear: PGear; withFall: boolean; Left, Right: LongInt; skipProximity: boolean); procedure FindPlace(var Gear: PGear; withFall: boolean; Left, Right: LongInt; skipProximity, deleteOnFail: boolean); procedure FindPlace(var Gear: PGear; withFall: boolean; Left, Right, Bottom: LongInt; skipProximity, deleteOnFail: boolean); @@ -48,8 +48,8 @@ function CheckGearNear(Kind: TGearType; X, Y: hwFloat; rX, rY: LongInt): PGear; function CheckGearNear(Gear: PGear; Kind: TGearType; rX, rY: LongInt): PGear; function CheckGearDrowning(var Gear: PGear): boolean; -procedure CheckCollision(Gear: PGear); inline; -procedure CheckCollisionWithLand(Gear: PGear); inline; +procedure CheckCollision(Gear: PGear); +procedure CheckCollisionWithLand(Gear: PGear); procedure AmmoShove(Ammo: PGear; Damage, Power: LongInt); procedure AmmoShoveCache(Ammo: PGear; Damage, Power: LongInt); @@ -63,7 +63,7 @@ procedure SetAllToActive; procedure SetAllHHToActive(Ice: boolean); -procedure SetAllHHToActive(); inline; +procedure SetAllHHToActive(); function GetAmmo(Hedgehog: PHedgehog): TAmmoType; function GetUtility(Hedgehog: PHedgehog): TAmmoType; @@ -84,9 +84,9 @@ uVariables, uLandGraphics, uScript, uStats, uCaptions, uTeams, uStore, uLocale, uTextures, uRenderUtils, uRandom, SDLh, uDebug, uGearsList, Math, uVisualGearsList, uGearsHandlersMess, - uGearsHedgehog; + uGearsHedgehog, uLandUtils; -procedure doMakeExplosion(X, Y, Radius: LongInt; AttackingHog: PHedgehog; Mask: Longword); inline; +procedure doMakeExplosion(X, Y, Radius: LongInt; AttackingHog: PHedgehog; Mask: Longword); begin doMakeExplosion(X, Y, Radius, AttackingHog, Mask, $FFFFFFFF); end; @@ -873,7 +873,7 @@ begin if (y and LAND_HEIGHT_MASK) = 0 then for i:= max(x - r, 0) to min(x + r, LAND_WIDTH - 1) do - if (Land[y, i] and mask <> 0) and (Land[y, i] and antimask = 0) then + if (LandGet(y, i) and mask <> 0) and (LandGet(y, i) and antimask = 0) then begin inc(count); if count = c then @@ -895,8 +895,8 @@ begin for i:= r - c + 2 to r do begin - if (Land[y, x - i] and mask <> 0) then inc(cnt); - if (Land[y, x + i] and mask <> 0) then inc(cnt); + if (LandGet(y, x - i) and mask <> 0) then inc(cnt); + if (LandGet(y, x + i) and mask <> 0) then inc(cnt); if cnt >= c then begin @@ -925,12 +925,12 @@ NoGearsToAvoid:= true end; -procedure FindPlace(var Gear: PGear; withFall: boolean; Left, Right: LongInt); inline; +procedure FindPlace(var Gear: PGear; withFall: boolean; Left, Right: LongInt); begin FindPlace(Gear, withFall, Left, Right, false, true); end; -procedure FindPlace(var Gear: PGear; withFall: boolean; Left, Right: LongInt; skipProximity: boolean); inline; +procedure FindPlace(var Gear: PGear; withFall: boolean; Left, Right: LongInt; skipProximity: boolean); begin FindPlace(Gear, withFall, Left, Right, skipProximity, true); end; @@ -965,7 +965,7 @@ repeat if GetRandom(2) = 0 then dir:= -1 else dir:= 1; x:= max(LAND_WIDTH div 2048, LongInt(GetRandom(Delta))); - if dir = 1 then x:= Left + x else x:= Right - x; + if dir = 1 then x:= Left + x else x:= Right - x; repeat cnt:= 0; y:= min(1024, topY) - Gear^.Radius shl 1; @@ -984,7 +984,7 @@ until (y >= Bottom) or (ignoreOverlap and (CountLand(x, y, Gear^.Radius - 1, 1, lfAll, 0) <> 0)) or - (not ignoreOverlap and + (not ignoreOverlap and (CountLand(x, y, Gear^.Radius - 1, 1, lfLandMask, 0) <> 0)); if (y - sy > Gear^.Radius * 2) and (y < Bottom) @@ -1172,7 +1172,7 @@ CheckGearNear := CheckGearNearImpl(Kind, Gear^.X, Gear^.Y, rX, rY, Gear); end; -procedure CheckCollision(Gear: PGear); inline; +procedure CheckCollision(Gear: PGear); begin if (TestCollisionXwithGear(Gear, hwSign(Gear^.dX)) <> 0) or (TestCollisionYwithGear(Gear, hwSign(Gear^.dY)) <> 0) then @@ -1181,7 +1181,7 @@ Gear^.State := Gear^.State and (not gstCollision) end; -procedure CheckCollisionWithLand(Gear: PGear); inline; +procedure CheckCollisionWithLand(Gear: PGear); begin if (TestCollisionX(Gear, hwSign(Gear^.dX)) <> 0) or (TestCollisionY(Gear, hwSign(Gear^.dY)) <> 0) then @@ -1413,8 +1413,8 @@ gtFirePunch, gtKamikaze, gtWhip, gtShover]) and (((Ammo^.Data <> nil) and (PGear(Ammo^.Data) = Gear)) or (not UpdateHitOrder( - Gear, - Ammo^.WDTimer, + Gear, + Ammo^.WDTimer, (Ammo^.Kind = gtMinigunBullet) and (Ammo^.Pos <> 0)))) then continue; @@ -1502,10 +1502,10 @@ else if ((Ammo^.Kind <> gtFlame) or (Gear^.Kind = gtHedgehog)) and (Power <> 0) then begin if (Ammo^.Kind in [gtMinigunBullet]) then - begin + begin Gear^.dX:= Gear^.dX + Ammo^.dX * Power * _0_01; Gear^.dY:= Gear^.dY + Ammo^.dY * Power * _0_01 - end + end else begin Gear^.dX:= Ammo^.dX * Power * _0_01; @@ -1591,7 +1591,7 @@ end end; -procedure SetAllHHToActive; inline; +procedure SetAllHHToActive; begin SetAllHHToActive(true) end; diff -r 2b4f361e3891 -r 629d5123a979 hedgewars/uInputHandler.pas --- a/hedgewars/uInputHandler.pas Sat Sep 28 22:27:13 2024 +0200 +++ b/hedgewars/uInputHandler.pas Tue Dec 31 15:19:43 2024 +0100 @@ -25,7 +25,7 @@ procedure initModule; procedure freeModule; -function KeyNameToCode(name: shortstring): LongInt; inline; +function KeyNameToCode(name: shortstring): LongInt; function KeyNameToCode(name: shortstring; Modifier: shortstring): LongInt; function KeyBindToCode(bind: shortstring): LongInt; @@ -36,7 +36,7 @@ procedure ProcessMouseMotion(xrel, yrel: LongInt); //procedure ProcessMouseWheel(x, y: LongInt); procedure ProcessMouseWheel(y: LongInt); -procedure ProcessKey(event: TSDL_KeyboardEvent); inline; +procedure ProcessKey(event: TSDL_KeyboardEvent); procedure ProcessKey(code: LongInt; KeyDown: boolean); {$IFDEF USE_AM_NUMCOLUMN} @@ -84,7 +84,7 @@ //ControllerHats: array[0..5] of array[0..19] of Byte; //ControllerButtons: array[0..5] of array[0..19] of Byte; -function KeyNameToCode(name: shortstring): LongInt; inline; +function KeyNameToCode(name: shortstring): LongInt; begin KeyNameToCode:= KeyNameToCode(name, ''); end; @@ -294,7 +294,7 @@ end end; -procedure ProcessKey(event: TSDL_KeyboardEvent); inline; +procedure ProcessKey(event: TSDL_KeyboardEvent); var code: LongInt; begin // TODO diff -r 2b4f361e3891 -r 629d5123a979 hedgewars/uLand.pas --- a/hedgewars/uLand.pas Sat Sep 28 22:27:13 2024 +0200 +++ b/hedgewars/uLand.pas Tue Dec 31 15:19:43 2024 +0100 @@ -32,19 +32,18 @@ implementation uses uConsole, uStore, uRandom, uLandObjects, uIO, uLandTexture, uVariables, uUtils, uCommands, adler32, uDebug, uLandPainted, uTextures, - uLandGenMaze, uPhysFSLayer, uScript, uLandGenPerlin, - uLandGenTemplateBased, uLandUtils, uRenderUtils; + uPhysFSLayer, uScript, uLandGenPerlin, + uLandUtils, uRenderUtils; var digest: shortstring; maskOnly: boolean; - procedure PrettifyLandAlpha(); begin if (cReducedQuality and rqBlurryLand) <> 0 then - PrettifyAlpha2D(LandPixels, LAND_HEIGHT div 2, LAND_WIDTH div 2) + PrettifyAlpha2D(LAND_HEIGHT div 2, LAND_WIDTH div 2) else - PrettifyAlpha2D(LandPixels, LAND_HEIGHT, LAND_WIDTH); + PrettifyAlpha2D(LAND_HEIGHT, LAND_WIDTH); end; procedure DrawBorderFromImage(Surface: PSDL_Surface); @@ -64,18 +63,18 @@ begin yd:= LAND_HEIGHT - 1; repeat - while (yd > 0) and ((Land[yd, x] and targetMask) = 0) do dec(yd); + while (yd > 0) and ((LandGet(yd, x) and targetMask) = 0) do dec(yd); if (yd < 0) then yd:= 0; - while (yd < LAND_HEIGHT) and ((Land[yd, x] and targetMask) <> 0) do + while (yd < LAND_HEIGHT) and ((LandGet(yd, x) and targetMask) <> 0) do inc(yd); dec(yd); yu:= yd; - while (yu > 0 ) and ((Land[yu, x] and targetMask) <> 0) do dec(yu); - while (yu < yd ) and ((Land[yu, x] and targetMask) = 0) do inc(yu); + while (yu > 0 ) and ((LandGet(yu, x) and targetMask) <> 0) do dec(yu); + while (yu < yd ) and ((LandGet(yu, x) and targetMask) = 0) do inc(yu); if (yd < LAND_HEIGHT - 1) and ((yd - yu) >= 16) then copyToXYFromRect(tmpsurf, Surface, x mod tmpsurf^.w, 16, 1, 16, x, yd - 15); @@ -100,7 +99,7 @@ for x:= 0 to LAND_WIDTH - 1 do for y:= 0 to LAND_HEIGHT - 1 do - if Land[y, x] = 0 then + if LandGet(y, x) = 0 then if s < y then begin for i:= max(s, y - 8) to y - 1 do @@ -108,9 +107,9 @@ if ((x + i) and 16) = 0 then c:= c1 else c:= c2; if (cReducedQuality and rqBlurryLand) = 0 then - LandPixels[i, x]:= c + LandPixelSet(i, x, c) else - LandPixels[i div 2, x div 2]:= c + LandPixelSet(i div 2, x div 2, c) end; s:= LAND_HEIGHT end @@ -123,9 +122,9 @@ if ((x + y) and 16) = 0 then c:= c1 else c:= c2; if (cReducedQuality and rqBlurryLand) = 0 then - LandPixels[y, x]:= c + LandPixelSet(y, x, c) else - LandPixels[y div 2, x div 2]:= c + LandPixelSet(y div 2, x div 2, c) end; end; @@ -134,7 +133,7 @@ for y:= 0 to LAND_HEIGHT - 1 do for x:= 0 to LAND_WIDTH - 1 do - if Land[y, x] = 0 then + if LandGet(y, x) = 0 then if s < x then begin for i:= max(s, x - 8) to x - 1 do @@ -142,9 +141,9 @@ if ((y + i) and 16) = 0 then c:= c1 else c:= c2; if (cReducedQuality and rqBlurryLand) = 0 then - LandPixels[y, i]:= c + LandPixelSet(y, i, c) else - LandPixels[y div 2, i div 2]:= c + LandPixelSet(y div 2, i div 2, c) end; s:= LAND_WIDTH end @@ -157,9 +156,9 @@ if ((x + y) and 16) = 0 then c:= c1 else c:= c2; if (cReducedQuality and rqBlurryLand) = 0 then - LandPixels[y, x]:= c + LandPixelSet(y, x, c) else - LandPixels[y div 2, x div 2]:= c + LandPixelSet(y div 2, x div 2, c) end; end end; @@ -356,15 +355,15 @@ uLandPainted.Draw; end; -function SelectTemplate: LongInt; +function SelectTemplate: shortstring; var l: LongInt; begin - SelectTemplate:= 0; + SelectTemplate:= ''; if (cReducedQuality and rqLowRes) <> 0 then - SelectTemplate:= SmallTemplates[getrandom(Succ(High(SmallTemplates)))] + SelectTemplate:= 'small' else begin - if cTemplateFilter = 0 then + if (cTemplateFilter = 0) and (cMapGen <> mgMaze) then begin l:= getRandom(GroupedTemplatesCount); repeat @@ -374,22 +373,35 @@ end else getRandom(1); - case cTemplateFilter of - 0: OutError('Error selecting TemplateFilter. Ask unC0Rr about what you did wrong', true); - 1: SelectTemplate:= SmallTemplates[getrandom(TemplateCounts[cTemplateFilter])]; - 2: SelectTemplate:= MediumTemplates[getrandom(TemplateCounts[cTemplateFilter])]; - 3: SelectTemplate:= LargeTemplates[getrandom(TemplateCounts[cTemplateFilter])]; - 4: SelectTemplate:= CavernTemplates[getrandom(TemplateCounts[cTemplateFilter])]; - 5: SelectTemplate:= WackyTemplates[getrandom(TemplateCounts[cTemplateFilter])]; - // For lua only! - 6: begin - SelectTemplate:= min(LuaTemplateNumber,High(EdgeTemplates)); - GetRandom(2) // burn 1 - end - end + case cMapGen of + mgMaze: + case cTemplateFilter of + 0: SelectTemplate:= 'small_tunnels'; + 1: SelectTemplate:= 'medium_tunnels'; + 2: SelectTemplate:= 'large_tunnels'; + 3: SelectTemplate:= 'small_islands'; + 4: SelectTemplate:= 'medium_islands'; + 5: SelectTemplate:= 'large_islands'; + else OutError('Error selecting TemplateFilter', true); + end + else + case cTemplateFilter of + 1: SelectTemplate:= 'small'; + 2: SelectTemplate:= 'medium'; + 3: SelectTemplate:= 'large'; + 4: SelectTemplate:= 'cavern'; + 5: SelectTemplate:= 'wacky'; + // For lua only! + 6: begin + SelectTemplate:= 'small'; + GetRandom(2) // burn 1 + end + else OutError('Error selecting TemplateFilter', true); + end + end end; - WriteLnToConsole('Selected template #'+inttostr(SelectTemplate)+' using filter #'+inttostr(cTemplateFilter)); + WriteLnToConsole('Using template filter '+SelectTemplate); end; procedure LandSurface2LandPixels(Surface: PSDL_Surface); @@ -405,11 +417,11 @@ for y:= 0 to LAND_HEIGHT - 1 do begin for x:= 0 to LAND_WIDTH - 1 do - if Land[y, x] <> 0 then + if LandGet(y, x) <> 0 then if (cReducedQuality and rqBlurryLand) = 0 then - LandPixels[y, x]:= p^[x]// or AMask + LandPixelSet(y, x, p^[x])// or AMask else - LandPixels[y div 2, x div 2]:= p^[x]; + LandPixelSet(y div 2, x div 2, p^[x]); p:= PLongwordArray(@(p^[Surface^.pitch div 4])); end; @@ -439,38 +451,38 @@ for x:= LongWord(leftX+2) to LongWord(rightX-2) do for y:= LongWord(topY+2) to LAND_HEIGHT-3 do - if (Land[y, x] = 0) and - (((Land[y, x-1] = lfBasic) and ((Land[y+1,x] = lfBasic)) or (Land[y-1,x] = lfBasic)) or - ((Land[y, x+1] = lfBasic) and ((Land[y-1,x] = lfBasic) or (Land[y+1,x] = lfBasic)))) then + if (LandGet(y, x) = 0) and + (((LandGet(y, x-1) = lfBasic) and ((LandGet(y+1,x) = lfBasic)) or (LandGet(y-1,x) = lfBasic)) or + ((LandGet(y, x+1) = lfBasic) and ((LandGet(y-1,x) = lfBasic) or (LandGet(y+1,x) = lfBasic)))) then begin if (cReducedQuality and rqBlurryLand) = 0 then begin - if (Land[y, x-1] = lfBasic) and (LandPixels[y, x-1] and AMask <> 0) then - LandPixels[y, x]:= LandPixels[y, x-1] + if (LandGet(y, x-1) = lfBasic) and (LandPixelGet(y, x-1) and AMask <> 0) then + LandPixelSet(y, x, LandPixelGet(y, x-1)) - else if (Land[y, x+1] = lfBasic) and (LandPixels[y, x+1] and AMask <> 0) then - LandPixels[y, x]:= LandPixels[y, x+1] + else if (LandGet(y, x+1) = lfBasic) and (LandPixelGet(y, x+1) and AMask <> 0) then + LandPixelSet(y, x, LandPixelGet(y, x+1)) - else if (Land[y-1, x] = lfBasic) and (LandPixels[y-1, x] and AMask <> 0) then - LandPixels[y, x]:= LandPixels[y-1, x] + else if (LandGet(y-1, x) = lfBasic) and (LandPixelGet(y-1, x) and AMask <> 0) then + LandPixelSet(y, x, LandPixelGet(y-1, x)) - else if (Land[y+1, x] = lfBasic) and (LandPixels[y+1, x] and AMask <> 0) then - LandPixels[y, x]:= LandPixels[y+1, x]; + else if (LandGet(y+1, x) = lfBasic) and (LandPixelGet(y+1, x) and AMask <> 0) then + LandPixelSet(y, x, LandPixelGet(y+1, x)); - if (((LandPixels[y,x] and AMask) shr AShift) > 10) then - LandPixels[y,x]:= (LandPixels[y,x] and (not AMask)) or (128 shl AShift) + if (((LandPixelGet(y,x) and AMask) shr AShift) > 10) then + LandPixelSet(y, x, (LandPixelGet(y,x) and (not AMask)) or (128 shl AShift)) end; - Land[y,x]:= lfObject + LandSet(y, x, lfObject) end - else if (Land[y, x] = 0) and - (((Land[y, x-1] = lfBasic) and (Land[y+1,x-1] = lfBasic) and (Land[y+2,x] = lfBasic)) or - ((Land[y, x-1] = lfBasic) and (Land[y-1,x-1] = lfBasic) and (Land[y-2,x] = lfBasic)) or - ((Land[y, x+1] = lfBasic) and (Land[y+1,x+1] = lfBasic) and (Land[y+2,x] = lfBasic)) or - ((Land[y, x+1] = lfBasic) and (Land[y-1,x+1] = lfBasic) and (Land[y-2,x] = lfBasic)) or - ((Land[y+1, x] = lfBasic) and (Land[y+1,x+1] = lfBasic) and (Land[y,x+2] = lfBasic)) or - ((Land[y-1, x] = lfBasic) and (Land[y-1,x+1] = lfBasic) and (Land[y,x+2] = lfBasic)) or - ((Land[y+1, x] = lfBasic) and (Land[y+1,x-1] = lfBasic) and (Land[y,x-2] = lfBasic)) or - ((Land[y-1, x] = lfBasic) and (Land[y-1,x-1] = lfBasic) and (Land[y,x-2] = lfBasic))) then + else if (LandGet(y, x) = 0) and + (((LandGet(y, x-1) = lfBasic) and (LandGet(y+1,x-1) = lfBasic) and (LandGet(y+2,x) = lfBasic)) or + ((LandGet(y, x-1) = lfBasic) and (LandGet(y-1,x-1) = lfBasic) and (LandGet(y-2,x) = lfBasic)) or + ((LandGet(y, x+1) = lfBasic) and (LandGet(y+1,x+1) = lfBasic) and (LandGet(y+2,x) = lfBasic)) or + ((LandGet(y, x+1) = lfBasic) and (LandGet(y-1,x+1) = lfBasic) and (LandGet(y-2,x) = lfBasic)) or + ((LandGet(y+1, x) = lfBasic) and (LandGet(y+1,x+1) = lfBasic) and (LandGet(y,x+2) = lfBasic)) or + ((LandGet(y-1, x) = lfBasic) and (LandGet(y-1,x+1) = lfBasic) and (LandGet(y,x+2) = lfBasic)) or + ((LandGet(y+1, x) = lfBasic) and (LandGet(y+1,x-1) = lfBasic) and (LandGet(y,x-2) = lfBasic)) or + ((LandGet(y-1, x) = lfBasic) and (LandGet(y-1,x-1) = lfBasic) and (LandGet(y,x-2) = lfBasic))) then begin @@ -478,22 +490,22 @@ begin - if (Land[y, x-1] = lfBasic) and (LandPixels[y,x-1] and AMask <> 0) then - LandPixels[y, x]:= LandPixels[y, x-1] + if (LandGet(y, x-1) = lfBasic) and (LandPixelGet(y,x-1) and AMask <> 0) then + LandPixelSet(y, x, LandPixelGet(y, x-1)) - else if (Land[y, x+1] = lfBasic) and (LandPixels[y,x+1] and AMask <> 0) then - LandPixels[y, x]:= LandPixels[y, x+1] + else if (LandGet(y, x+1) = lfBasic) and (LandPixelGet(y,x+1) and AMask <> 0) then + LandPixelSet(y, x, LandPixelGet(y, x+1)) - else if (Land[y+1, x] = lfBasic) and (LandPixels[y+1,x] and AMask <> 0) then - LandPixels[y, x]:= LandPixels[y+1, x] + else if (LandGet(y+1, x) = lfBasic) and (LandPixelGet(y+1,x) and AMask <> 0) then + LandPixelSet(y, x, LandPixelGet(y+1, x)) - else if (Land[y-1, x] = lfBasic) and (LandPixels[y-1,x] and AMask <> 0) then - LandPixels[y, x]:= LandPixels[y-1, x]; + else if (LandGet(y-1, x) = lfBasic) and (LandPixelGet(y-1,x) and AMask <> 0) then + LandPixelSet(y, x, LandPixelGet(y-1, x)); - if (((LandPixels[y,x] and AMask) shr AShift) > 10) then - LandPixels[y,x]:= (LandPixels[y,x] and (not AMask)) or (64 shl AShift) + if (((LandPixelGet(y,x) and AMask) shr AShift) > 10) then + LandPixelSet(y, x, (LandPixelGet(y,x) and (not AMask)) or (64 shl AShift)) end; - Land[y,x]:= lfObject + LandSet(y, x, lfObject) end; AddProgress(); @@ -527,8 +539,8 @@ begin if (y <= wbm) and ((x - w1) mod (bmWidth * 2) >= bmWidth) then continue; - Land[y,x]:= lfBasic; - Land[y,lastX-x]:= lfBasic; + LandSet(y, x, lfBasic); + LandSet(y, lastX - x, lfBasic); end; end; @@ -545,8 +557,8 @@ // align battlement on inner edge, because real outer edge could be offscreen if (y <= wbm) and ((LAND_WIDTH + x - bmref) mod (bmWidth * 2) >= bmWidth) then continue; - Land[y,x]:= lfBasic; - Land[y,lastX-x]:= lfBasic; + LandSet(y, x, lfBasic); + LandSet(y, lastX - x, lfBasic); end; end; end; @@ -691,7 +703,7 @@ for y:= 0 to Pred(tmpsurf^.h) do begin for x:= 0 to Pred(tmpsurf^.w) do - SetLand(Land[cpY + y, cpX + x], p^[x]); + SetLand(cpY + y, cpX + x, p^[x]); p:= PLongwordArray(@(p^[tmpsurf^.pitch div 4])); end; @@ -756,16 +768,16 @@ for x:= LongWord(leftX) to LongWord(rightX) do begin y:= Longword(cWaterLine) - 1 - w; - Land[y, x]:= lfIndestructible; + LandSet(y, x, lfIndestructible); if (x + y) mod 32 < 16 then c:= AMask else c:= AMask or RMask or GMask; // FF00FFFF if (cReducedQuality and rqBlurryLand) = 0 then - LandPixels[y, x]:= c + LandPixelSet(y, x, c) else - LandPixels[y div 2, x div 2]:= c + LandPixelSet(y div 2, x div 2, c) end end; @@ -794,15 +806,16 @@ begin WriteLnToConsole('Generating land...'); case cMapGen of - mgRandom: GenTemplated(EdgeTemplates[SelectTemplate]); - mgMaze : begin ResizeLand(4096,2048); GenMaze; end; + mgRandom: GenerateOutlineTemplatedLand(cFeatureSize, cSeed, SelectTemplate, PathPrefix); + mgMaze : GenerateMazeLand(cFeatureSize, cSeed, SelectTemplate, PathPrefix); mgPerlin: begin ResizeLand(4096,2048); GenPerlin; end; mgDrawn : GenDrawnMap; mgForts : begin GameFlags:= (GameFlags or gfDivideTeams); MakeFortsMap(); end; + mgWfc: GenerateWfcTemplatedLand(cFeatureSize, cSeed, SelectTemplate, PathPrefix); else OutError('Unknown mapgen', true); end; - if cMapGen <> mgForts then + if (cMapGen <> mgForts) then GenLandSurface end; @@ -815,7 +828,7 @@ else for y:= LongWord(topY) to LongWord(topY + 5) do for x:= LongWord(leftX) to LongWord(rightX) do - if Land[y, x] <> 0 then + if LandGet(y, x) <> 0 then begin inc(c); if c > LongWord((LAND_WIDTH div 2)) then // avoid accidental triggering @@ -834,13 +847,13 @@ for y:= 0 to LAND_HEIGHT - 1 do for x:= 0 to LAND_WIDTH - 1 do if (y < LongWord(topY)) or (x < LongWord(leftX)) or (x > LongWord(rightX)) then - Land[y, x]:= lfIndestructible; + LandSet(y, x, lfIndestructible); end else if topY > 0 then begin for y:= 0 to LongWord(topY - 1) do for x:= 0 to LAND_WIDTH - 1 do - Land[y, x]:= lfIndestructible; + LandSet(y, x, lfIndestructible); end; // Render map border for w:= 0 to (cBorderWidth-1) do @@ -850,8 +863,8 @@ for y:= LongWord(topY) to LAND_HEIGHT - 1 do begin // set land flags - Land[y, leftX + w]:= lfIndestructible; - Land[y, rightX - w]:= lfIndestructible; + LandSet(y, leftX + w, lfIndestructible); + LandSet(y, rightX - w, lfIndestructible); // paint black and yellow stripes if (y + leftX + w) mod 32 < 16 then @@ -865,29 +878,29 @@ if (cReducedQuality and rqBlurryLand) = 0 then begin - LandPixels[y, leftX + w]:= c; - LandPixels[y, rightX - w]:= c2; + LandPixelSet(y, leftX + w, c); + LandPixelSet(y, rightX - w, c2); end else begin - LandPixels[y div 2, (leftX + w) div 2]:= c; - LandPixels[y div 2, (rightX - w) div 2]:= c2; + LandPixelSet(y div 2, (leftX + w) div 2, c); + LandPixelSet(y div 2, (rightX - w) div 2, c2); end; end; // Top border for x:= LongWord(leftX) to LongWord(rightX) do begin - Land[topY + w, x]:= lfIndestructible; + LandSet(topY + w, x, lfIndestructible); if (topY + x + w) mod 32 < 16 then c:= AMask // black else c:= AMask or RMask or GMask; // yellow if (cReducedQuality and rqBlurryLand) = 0 then - LandPixels[topY + w, x]:= c + LandPixelSet(topY + w, x, c) else - LandPixels[(topY + w) div 2, x div 2]:= c; + LandPixelSet((topY + w) div 2, x div 2, c); end; end; end; @@ -915,23 +928,23 @@ for x:= LongWord(leftX) to LongWord(rightX) do for y:= LongWord(topY) to LAND_HEIGHT-1 do begin - w:= LandPixels[y,x]; + w:= LandPixelGet(y,x); w:= round(((w shr RShift and $FF) * RGB_LUMINANCE_RED + (w shr BShift and $FF) * RGB_LUMINANCE_GREEN + (w shr GShift and $FF) * RGB_LUMINANCE_BLUE)); if w > 255 then w:= 255; - w:= (w and $FF shl RShift) or (w and $FF shl BShift) or (w and $FF shl GShift) or (LandPixels[y,x] and AMask); - LandPixels[y,x]:= w or (LandPixels[y, x] and AMask) + w:= (w and $FF shl RShift) or (w and $FF shl BShift) or (w and $FF shl GShift) or (LandPixelGet(y,x) and AMask); + LandPixelSet(y, x, w or (LandPixelGet(y, x) and AMask)) end else for x:= LongWord(leftX div 2) to LongWord(rightX div 2) do for y:= LongWord(topY div 2) to LAND_HEIGHT-1 div 2 do begin - w:= LandPixels[y div 2,x div 2]; + w:= LandPixelGet(y div 2,x div 2); w:= ((w shr RShift and $FF) + (w shr BShift and $FF) + (w shr GShift and $FF)) div 3; - w:= (w and $FF shl RShift) or (w and $FF shl BShift) or (w and $FF shl GShift) or (LandPixels[y div 2,x div 2] and AMask); - LandPixels[y,x]:= w or (LandPixels[y div 2, x div 2] and AMask) + w:= (w and $FF shl RShift) or (w and $FF shl BShift) or (w and $FF shl GShift) or (LandPixelGet(y div 2,x div 2) and AMask); + LandPixelSet(y, x, w or (LandPixelGet(y div 2, x div 2) and AMask)) end end; @@ -949,11 +962,12 @@ begin WriteLnToConsole('Generating preview...'); case cMapGen of - mgRandom: GenTemplated(EdgeTemplates[SelectTemplate]); - mgMaze: begin ResizeLand(4096,2048); GenMaze; end; + mgRandom: GenerateOutlineTemplatedLand(cFeatureSize, cSeed, SelectTemplate, PathPrefix); + mgMaze: GenerateMazeLand(cFeatureSize, cSeed, SelectTemplate, PathPrefix); mgPerlin: begin ResizeLand(4096,2048); GenPerlin; end; mgDrawn: begin GenDrawnMap; end; mgForts: MakeFortsPreview(); + mgWfc: GenerateWfcTemplatedLand(cFeatureSize, cSeed, SelectTemplate, PathPrefix); else OutError('Unknown mapgen', true); end; @@ -994,7 +1008,7 @@ for yy:= y * lh to y * lh + 7 do for xx:= x * lw + cbit to x * lw + cbit + 7 do if ((yy-oy) and LAND_HEIGHT_MASK = 0) and ((xx-ox) and LAND_WIDTH_MASK = 0) - and (Land[yy-oy, xx-ox] <> 0) then + and (LandGet(yy-oy, xx-ox) <> 0) then inc(t); if t > 8 then Preview[y, x]:= Preview[y, x] or ($80 shr bit); @@ -1008,11 +1022,12 @@ begin WriteLnToConsole('Generating preview...'); case cMapGen of - mgRandom: GenTemplated(EdgeTemplates[SelectTemplate]); - mgMaze: begin ResizeLand(4096,2048); GenMaze; end; + mgRandom: GenerateOutlineTemplatedLand(cFeatureSize, cSeed, SelectTemplate, PathPrefix); + mgMaze: GenerateMazeLand(cFeatureSize, cSeed, SelectTemplate, PathPrefix); mgPerlin: begin ResizeLand(4096,2048); GenPerlin; end; mgDrawn: begin GenDrawnMap; end; mgForts: MakeFortsPreview; + mgWfc: GenerateWfcTemplatedLand(cFeatureSize, cSeed, SelectTemplate, PathPrefix); else OutError('Unknown mapgen', true); end; @@ -1052,7 +1067,7 @@ for yy:= y * lh - oy to y * lh + lh - 1 - oy do for xx:= x * lw - ox to x * lw + lw - 1 - ox do if (yy and LAND_HEIGHT_MASK = 0) and (xx and LAND_WIDTH_MASK = 0) - and (Land[yy, xx] <> 0) then + and (LandGet(yy, xx) <> 0) then inc(t); Preview[y, x]:= t * 255 div (lh * lw); @@ -1074,7 +1089,7 @@ begin landPixelDigest:= 1; for i:= 0 to LAND_HEIGHT-1 do - landPixelDigest:= Adler32Update(landPixelDigest, @Land[i,0], LAND_WIDTH*2); + landPixelDigest:= Adler32Update(landPixelDigest, LandRow(i), LAND_WIDTH*2); s:= 'M' + IntToStr(syncedPixelDigest)+'|'+IntToStr(landPixelDigest); ScriptSetString('LandDigest',IntToStr(landPixelDigest)); @@ -1093,21 +1108,11 @@ maskOnly:= false; LAND_WIDTH:= 0; LAND_HEIGHT:= 0; -(* - if (cReducedQuality and rqBlurryLand) = 0 then - SetLength(LandPixels, LAND_HEIGHT, LAND_WIDTH) - else - SetLength(LandPixels, LAND_HEIGHT div 2, LAND_WIDTH div 2); - - SetLength(Land, LAND_HEIGHT, LAND_WIDTH); - SetLength(LandDirty, (LAND_HEIGHT div 32), (LAND_WIDTH div 32)); -*) end; procedure freeModule; begin - SetLength(Land, 0, 0); - SetLength(LandPixels, 0, 0); + DisposeLand; SetLength(LandDirty, 0, 0); end; diff -r 2b4f361e3891 -r 629d5123a979 hedgewars/uLandGenMaze.pas --- a/hedgewars/uLandGenMaze.pas Sat Sep 28 22:27:13 2024 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,546 +0,0 @@ -{$INCLUDE "options.inc"} - -unit uLandGenMaze; - -interface - -procedure GenMaze; - -implementation - -uses uRandom, uLandOutline, uLandTemplates, uVariables, uFloat, uConsts, uLandGenTemplateBased, uUtils; - -type direction = record x, y: LongInt; end; -const DIR_N: direction = (x: 0; y: -1); - DIR_E: direction = (x: 1; y: 0); - DIR_S: direction = (x: 0; y: 1); - DIR_W: direction = (x: -1; y: 0); - -operator = (const a, b: direction) c: Boolean; -begin - c := (a.x = b.x) and (a.y = b.y); -end; - -const small_cell_size = 128; - medium_cell_size = 192; - large_cell_size = 256; - braidness = 10; - -type - cell_t = record x,y : LongInt - end; - -var x, y : LongInt; - cellsize : LongInt; //selected by the user in the gui - seen_cells_x, seen_cells_y : LongInt; //number of cells that can be visited by the generator, that is every second cell in x and y direction. the cells between there are walls that will be removed when we move from one cell to another - num_edges_x, num_edges_y : LongInt; //number of resulting edges that need to be vertexificated - num_cells_x, num_cells_y : LongInt; //actual number of cells, depending on cell size - - - seen_list : array of array of LongInt; - xwalls : array of array of Boolean; - ywalls : array of array of Boolean; - x_edge_list : array of array of Boolean; - y_edge_list : array of array of Boolean; - maze : array of array of Boolean; - - pa : TPixAr; - num_vertices : LongInt; - off_y : LongInt; - num_steps : LongInt; - current_step : LongInt; - - step_done : array of Boolean; - - done : Boolean; - -{ last_cell : array 0..3 of record x, y :LongInt ; end; - came_from : array of array of record x, y: LongInt; end; - came_from_pos : array of LongInt; -} - last_cell : array of cell_t; - came_from : array of array of cell_t; - came_from_pos: array of LongInt; - - maze_inverted : Boolean; - -function when_seen(x: LongInt; y: LongInt): LongInt; -begin -if (x < 0) or (x >= seen_cells_x) or (y < 0) or (y >= seen_cells_y) then - when_seen := current_step -else - when_seen := seen_list[x, y]; -end; - -function is_x_edge(x, y: LongInt): Boolean; -begin -if (x < 0) or (x > num_edges_x) or (y < 0) or (y > num_cells_y) then - is_x_edge := false -else - is_x_edge := x_edge_list[x, y]; -end; - -function is_y_edge(x, y: LongInt): Boolean; -begin -if (x < 0) or (x > num_cells_x) or (y < 0) or (y > num_edges_y) then - is_y_edge := false -else - is_y_edge := y_edge_list[x, y]; -end; - -procedure see_cell; -var dir: direction; - tries: LongInt; - x, y: LongInt; - found_cell: Boolean; - next_dir_clockwise: Boolean; - -begin -x := last_cell[current_step].x; -y := last_cell[current_step].y; -seen_list[x, y] := current_step; -case GetRandom(4) of - 0: dir := DIR_N; - 1: dir := DIR_E; - 2: dir := DIR_S; - 3: dir := DIR_W; -end; -tries := 0; -found_cell := false; -if getrandom(2) = 1 then - next_dir_clockwise := true -else - next_dir_clockwise := false; - -while (tries < 5) and (not found_cell) do -begin - if when_seen(x + dir.x, y + dir.y) = current_step then //we are seeing ourselves, try another direction - begin - //we have already seen the target cell, decide if we should remove the wall anyway - //(or put a wall there if maze_inverted, but we are not doing that right now) - if (not maze_inverted) and (GetRandom(braidness) = 0) then - //or just warn that inverted+braid+indestructible terrain != good idea - begin - case dir.x of - - -1: - if x > 0 then - ywalls[x-1, y] := false; - 1: - if x < seen_cells_x - 1 then - ywalls[x, y] := false; - end; - case dir.y of - -1: - if y > 0 then - xwalls[x, y-1] := false; - 1: - if y < seen_cells_y - 1 then - xwalls[x, y] := false; - end; - end; - if next_dir_clockwise then - begin - if dir = DIR_N then - dir := DIR_E - else if dir = DIR_E then - dir := DIR_S - else if dir = DIR_S then - dir := DIR_W - else - dir := DIR_N; - end - else - begin - if dir = DIR_N then - dir := DIR_W - else if dir = DIR_E then - dir := DIR_N - else if dir = DIR_S then - dir := DIR_E - else - dir := DIR_S; - end - end - else if when_seen(x + dir.x, y + dir.y) = -1 then //cell was not seen yet, go there - begin - case dir.y of - -1: xwalls[x, y-1] := false; - 1: xwalls[x, y] := false; - end; - case dir.x of - -1: ywalls[x-1, y] := false; - 1: ywalls[x, y] := false; - end; - last_cell[current_step].x := x+dir.x; - last_cell[current_step].y := y+dir.y; - came_from_pos[current_step] := came_from_pos[current_step] + 1; - came_from[current_step, came_from_pos[current_step]].x := x; - came_from[current_step, came_from_pos[current_step]].y := y; - found_cell := true; - end - else //we are seeing someone else, quit - begin - step_done[current_step] := true; - found_cell := true; - end; - - tries := tries + 1; -end; -if not found_cell then - begin - last_cell[current_step].x := came_from[current_step, came_from_pos[current_step]].x; - last_cell[current_step].y := came_from[current_step, came_from_pos[current_step]].y; - came_from_pos[current_step] := came_from_pos[current_step] - 1; - - if came_from_pos[current_step] >= 0 then - see_cell() - - else - step_done[current_step] := true; - end; -end; - -procedure add_vertex(x, y: LongInt); -var tmp_x, tmp_y, nx, ny: LongInt; -begin - if x = NTPX then - begin - if pa.ar[num_vertices - 6].x = NTPX then - begin - num_vertices := num_vertices - 6; - end - else - begin - pa.ar[num_vertices].x := NTPX; - pa.ar[num_vertices].y := 0; - end - end - else - begin - if maze_inverted or (x mod 2 = 0) then - tmp_x := cellsize - else - tmp_x := cellsize * 2 div 3; - - if maze_inverted or (y mod 2 = 0) then - tmp_y := cellsize - else - tmp_y := cellsize * 2 div 3; - - nx:= (x-1)*cellsize + tmp_x; - ny:= (y-1)*cellsize + tmp_y + off_y; - - if num_vertices > 2 then - if ((pa.ar[num_vertices - 2].x = pa.ar[num_vertices - 1].x) and (pa.ar[num_vertices - 1].x = nx)) - or ((pa.ar[num_vertices - 2].y = pa.ar[num_vertices - 1].y) and (pa.ar[num_vertices - 1].y = ny)) - then - dec(num_vertices); - - pa.ar[num_vertices].x := nx; - pa.ar[num_vertices].y := ny; - end; - - num_vertices := num_vertices + 1; -end; - -procedure add_edge(x, y: LongInt; dir: direction); -var i: LongInt; -begin -if dir = DIR_N then - begin - dir := DIR_W - end -else if dir = DIR_E then - begin - dir := DIR_N - end -else if dir = DIR_S then - begin - dir := DIR_E - end -else - begin - dir := DIR_S; - end; - -for i := 0 to 3 do - begin - if dir = DIR_N then - dir := DIR_E - else if dir = DIR_E then - dir := DIR_S - else if dir = DIR_S then - dir := DIR_W - else - dir := DIR_N; - - if (dir = DIR_N) and is_x_edge(x, y) then - begin - x_edge_list[x, y] := false; - add_vertex(x+1, y); - add_edge(x, y-1, DIR_N); - break; - end; - - if (dir = DIR_E) and is_y_edge(x+1, y) then - begin - y_edge_list[x+1, y] := false; - add_vertex(x+2, y+1); - add_edge(x+1, y, DIR_E); - break; - end; - - if (dir = DIR_S) and is_x_edge(x, y+1) then - begin - x_edge_list[x, y+1] := false; - add_vertex(x+1, y+2); - add_edge(x, y+1, DIR_S); - break; - end; - - if (dir = DIR_W) and is_y_edge(x, y) then - begin - y_edge_list[x, y] := false; - add_vertex(x, y+1); - add_edge(x-1, y, DIR_W); - break; - end; - end; - -end; - -procedure GenMaze; -var i: Longword; -begin -case cTemplateFilter of - 0: begin - cellsize := small_cell_size; - maze_inverted := false; - minDistance:= max(cFeatureSize*8,32); - dabDiv:= 150; - end; - 1: begin - cellsize := medium_cell_size; - minDistance:= max(cFeatureSize*6,20); - maze_inverted := false; - dabDiv:= 100; - end; - 2: begin - cellsize := large_cell_size; - minDistance:= max(cFeatureSize*5,12); - maze_inverted := false; - dabDiv:= 90; - end; - 3: begin - cellsize := small_cell_size; - minDistance:= max(cFeatureSize*8,32); - maze_inverted := true; - dabDiv:= 130; - end; - 4: begin - cellsize := medium_cell_size; - minDistance:= max(cFeatureSize*6,20); - maze_inverted := true; - dabDiv:= 100; - end; - 5: begin - cellsize := large_cell_size; - minDistance:= max(cFeatureSize*5,12); - maze_inverted := true; - dabDiv:= 85; - end; - end; - -num_cells_x := LAND_WIDTH div cellsize; -if not odd(num_cells_x) then - num_cells_x := num_cells_x - 1; //needs to be odd - -num_cells_y := LAND_HEIGHT div cellsize; -if not odd(num_cells_y) then - num_cells_y := num_cells_y - 1; - -num_edges_x := num_cells_x - 1; -num_edges_y := num_cells_y - 1; - -seen_cells_x := num_cells_x div 2; -seen_cells_y := num_cells_y div 2; - -if maze_inverted then - num_steps := 3 //TODO randomize, between 3 and 5? -else - num_steps := 1; - -SetLength(step_done, num_steps); -SetLength(last_cell, num_steps); -SetLength(came_from_pos, num_steps); -SetLength(came_from, num_steps, num_cells_x*num_cells_y); - -done := false; - -for current_step := 0 to num_steps - 1 do - begin - step_done[current_step] := false; - came_from_pos[current_step] := 0; - end; - -current_step := 0; - - -SetLength(seen_list, seen_cells_x, seen_cells_y); -SetLength(xwalls, seen_cells_x, seen_cells_y - 1); -SetLength(ywalls, seen_cells_x - 1, seen_cells_y); -SetLength(x_edge_list, num_edges_x, num_cells_y); -SetLength(y_edge_list, num_cells_x, num_edges_y); -SetLength(maze, num_cells_x, num_cells_y); - - -num_vertices := 0; - -playHeight := num_cells_y * cellsize; -playWidth := num_cells_x * cellsize; -off_y := LAND_HEIGHT - playHeight; - -for x := 0 to playWidth do - for y := 0 to off_y - 1 do - Land[y, x] := 0; - -for x := 0 to playWidth do - for y := off_y to LAND_HEIGHT - 1 do - Land[y, x] := lfBasic; - -for y := 0 to num_cells_y - 1 do - for x := 0 to num_cells_x - 1 do - maze[x, y] := false; - -for x := 0 to seen_cells_x - 1 do - for y := 0 to seen_cells_y - 2 do - xwalls[x, y] := true; - -for x := 0 to seen_cells_x - 2 do - for y := 0 to seen_cells_y - 1 do - ywalls[x, y] := true; - -for x := 0 to seen_cells_x - 1 do - for y := 0 to seen_cells_y - 1 do - seen_list[x, y] := -1; - -for x := 0 to num_edges_x - 1 do - for y := 0 to num_cells_y - 1 do - x_edge_list[x, y] := false; - -for x := 0 to num_cells_x - 1 do - for y := 0 to num_edges_y - 1 do - y_edge_list[x, y] := false; - -for current_step := 0 to num_steps-1 do - begin - x := GetRandom(seen_cells_x - 1) div LongWord(num_steps); - last_cell[current_step].x := x + current_step * seen_cells_x div num_steps; - last_cell[current_step].y := GetRandom(seen_cells_y); -end; - -while not done do - begin - done := true; - for current_step := 0 to num_steps-1 do - begin - if not step_done[current_step] then - begin - see_cell; - done := false; - end; - end; - end; - -for x := 0 to seen_cells_x - 1 do - for y := 0 to seen_cells_y - 1 do - if seen_list[x, y] > -1 then - maze[(x+1)*2-1, (y+1)*2-1] := true; - -for x := 0 to seen_cells_x - 1 do - for y := 0 to seen_cells_y - 2 do - if not xwalls[x, y] then - maze[x*2 + 1, y*2 + 2] := true; - - -for x := 0 to seen_cells_x - 2 do - for y := 0 to seen_cells_y - 1 do - if not ywalls[x, y] then - maze[x*2 + 2, y*2 + 1] := true; - -for x := 0 to num_edges_x - 1 do - for y := 0 to num_cells_y - 1 do - if maze[x, y] xor maze[x+1, y] then - x_edge_list[x, y] := true - else - x_edge_list[x, y] := false; - -for x := 0 to num_cells_x - 1 do - for y := 0 to num_edges_y - 1 do - if maze[x, y] xor maze[x, y+1] then - y_edge_list[x, y] := true - else - y_edge_list[x, y] := false; - -for x := 0 to num_edges_x - 1 do - for y := 0 to num_cells_y - 1 do - if x_edge_list[x, y] then - begin - x_edge_list[x, y] := false; - add_vertex(x+1, y+1); - add_vertex(x+1, y); - add_edge(x, y-1, DIR_N); - add_vertex(NTPX, 0); - end; - -pa.count := num_vertices; - -leftX:= 0; -rightX:= playWidth; -topY:= off_y; - -// fill point -pa.ar[pa.Count].x:= 1; -pa.ar[pa.Count].y:= 1 + off_y; - -{ -for i:= 0 to pa.Count - 1 do - begin - system.writeln(pa.ar[i].x, ', ', pa.ar[i].y); - end; -} - -// divide while it divides -repeat - i:= pa.Count; - DivideEdges(1, pa) -until i = pa.Count; - -// make it smooth -BezierizeEdge(pa, _0_2); - -DrawEdge(pa, 0); - -if maze_inverted then - FillLand(1, 1 + off_y, 0, 0) -else - begin - x := 0; - while Land[cellsize div 2 + cellsize + off_y, x] = lfBasic do - x := x + 1; - while Land[cellsize div 2 + cellsize + off_y, x] = 0 do - x := x + 1; - FillLand(x+1, cellsize div 2 + cellsize + off_y, 0, 0); - end; - -MaxHedgehogs:= 32; -if (GameFlags and gfDisableGirders) <> 0 then - hasGirders:= false -else - hasGirders := true; - -hasBorder := false; -end; - -end. diff -r 2b4f361e3891 -r 629d5123a979 hedgewars/uLandGenPerlin.pas --- a/hedgewars/uLandGenPerlin.pas Sat Sep 28 22:27:13 2024 +0200 +++ b/hedgewars/uLandGenPerlin.pas Tue Dec 31 15:19:43 2024 +0100 @@ -9,8 +9,8 @@ uses uVariables , uConsts , uRandom - , uLandOutline // FillLand , uUtils + , uLandUtils ; var p: array[0..511] of LongInt; @@ -39,7 +39,7 @@ 4086, 4088, 4089, 4091, 4092, 4092, 4093, 4094, 4094, 4095, 4095, 4095, 4095, 4095, 4095, 4095); -function fade(t: LongInt) : LongInt; inline; +function fade(t: LongInt) : LongInt; var t0, t1: LongInt; begin t0:= fadear[t shr 8]; @@ -53,13 +53,13 @@ end; -function lerp(t, a, b: LongInt) : LongInt; inline; +function lerp(t, a, b: LongInt) : LongInt; begin lerp:= a + ((Int64(b) - a) * t shr 12) end; -function grad(hash, x, y: LongInt) : LongInt; inline; +function grad(hash, x, y: LongInt) : LongInt; var h, v, u: LongInt; begin h:= hash and 15; @@ -74,7 +74,7 @@ end; -function inoise(x, y: LongInt) : LongInt; inline; +function inoise(x, y: LongInt) : LongInt; const N = $10000; var xx, yy, u, v, A, AA, AB, B, BA, BB: LongInt; begin @@ -205,24 +205,24 @@ } if r < rCutoff then - Land[y, x]:= 0 + LandSet(y, x, 0) else if param1 = 0 then - Land[y, x]:= lfObjMask + LandSet(y, x, lfObjMask) else - Land[y, x]:= lfBasic + LandSet(y, x, lfBasic) end; end; if param1 = 0 then begin for x:= 0 to width do - if Land[height - 1, x] = lfObjMask then FillLand(x, height - 1, 0, lfBasic); + if LandGet(height - 1, x) = lfObjMask then FillLand(x, height - 1, 0, lfBasic); // strip all lfObjMask pixels for y:= minY to LAND_HEIGHT - 1 do for x:= 0 to LAND_WIDTH - 1 do - if Land[y, x] = lfObjMask then - Land[y, x]:= 0; + if LandGet(y, x) = lfObjMask then + LandSet(y, x, 0); end; playWidth:= width; diff -r 2b4f361e3891 -r 629d5123a979 hedgewars/uLandGenTemplateBased.pas --- a/hedgewars/uLandGenTemplateBased.pas Sat Sep 28 22:27:13 2024 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,383 +0,0 @@ -unit uLandGenTemplateBased; -interface - -uses uLandTemplates, uLandOutline; - -procedure GenTemplated(var Template: TEdgeTemplate); -procedure DivideEdges(fillPointsCount: LongWord; var pa: TPixAr); - -var minDistance, dabDiv: LongInt; // different details size - -implementation -uses {$IFDEF IPHONEOS}uTypes, {$ENDIF} uVariables, uConsts, uFloat, uLandUtils, uRandom, SDLh, math; - - -procedure SetPoints(var Template: TEdgeTemplate; var pa: TPixAr; fps: PPointArray); -var i: LongInt; -begin - with Template do - begin - pa.Count:= BasePointsCount; - for i:= 0 to pred(LongInt(pa.Count)) do - begin - pa.ar[i].x:= BasePoints^[i].x + LongInt(GetRandom(BasePoints^[i].w)); - if pa.ar[i].x <> NTPX then - pa.ar[i].x:= pa.ar[i].x + ((LAND_WIDTH - Template.TemplateWidth) div 2); - pa.ar[i].y:= BasePoints^[i].y + LongInt(GetRandom(BasePoints^[i].h)) + LAND_HEIGHT - LongInt(Template.TemplateHeight) - end; - - if canMirror then - if getrandom(2) = 0 then - begin - for i:= 0 to pred(BasePointsCount) do - if pa.ar[i].x <> NTPX then - pa.ar[i].x:= LAND_WIDTH - 1 - pa.ar[i].x; - for i:= 0 to pred(FillPointsCount) do - fps^[i].x:= LAND_WIDTH - 1 - fps^[i].x; - end; - -(* Experiment in making this option more useful - if ((not isNegative) and (cTemplateFilter = 4)) or - (canFlip and (getrandom(2) = 0)) then - begin - for i:= 0 to pred(BasePointsCount) do - begin - pa.ar[i].y:= LAND_HEIGHT - 1 - pa.ar[i].y + (LAND_HEIGHT - TemplateHeight) * 2; - if pa.ar[i].y > LAND_HEIGHT - 1 then - pa.ar[i].y:= LAND_HEIGHT - 1; - end; - for i:= 0 to pred(FillPointsCount) do - begin - FillPoints^[i].y:= LAND_HEIGHT - 1 - FillPoints^[i].y + (LAND_HEIGHT - TemplateHeight) * 2; - if FillPoints^[i].y > LAND_HEIGHT - 1 then - FillPoints^[i].y:= LAND_HEIGHT - 1; - end; - end; - end -*) -// template recycling. Pull these off the floor a bit - if (not isNegative) and (cTemplateFilter = 4) then - begin - for i:= 0 to pred(BasePointsCount) do - begin - dec(pa.ar[i].y, 100); - if pa.ar[i].y < 0 then - pa.ar[i].y:= 0; - end; - for i:= 0 to pred(FillPointsCount) do - begin - dec(fps^[i].y, 100); - if fps^[i].y < 0 then - fps^[i].y:= 0; - end; - end; - - if (canFlip and (getrandom(2) = 0)) then - begin - for i:= 0 to pred(BasePointsCount) do - pa.ar[i].y:= LAND_HEIGHT - 1 - pa.ar[i].y; - for i:= 0 to pred(FillPointsCount) do - fps^[i].y:= LAND_HEIGHT - 1 - fps^[i].y; - end; - end -end; - -procedure FindPoint(si: LongInt; fillPointsCount: LongWord; var newPoint: TPoint; var pa: TPixAr); -const mapBorderMargin = 40; -var p1, p2, p4, fp, mp: TPoint; - i, t1, t2, iy, ix, aqpb: LongInt; - a, b, p, q: LongInt; - dab, d, distL, distR: LongInt; -begin - // [p1, p2] is the segment we're trying to divide - p1:= pa.ar[si]; - p2:= pa.ar[si + 1]; - - if p2.x = NTPX then - // it is segment from last to first point, so need to find first point - begin - i:= si - 2; - while (i >= 0) and (pa.ar[i].x <> NTPX) do - dec(i); - p2:= pa.ar[i + 1] - end; - - // perpendicular vector - a:= p2.y - p1.y; - b:= p1.x - p2.x; - dab:= DistanceI(a, b).Round; - - // its middle point - mp.x:= (p1.x + p2.x) div 2; - mp.y:= (p1.y + p2.y) div 2; - - // don't process too short segments or those which are too close to map borders - if (p1.x = NTPX) - or (dab < minDistance * 3) - or (mp.x < leftX + mapBorderMargin) - or (mp.x > rightX - mapBorderMargin) - or (mp.y < topY + mapBorderMargin) - or (mp.y > LongInt(LAND_HEIGHT) - mapBorderMargin) - then - begin - newPoint:= p1; - exit; - end; - - // find distances to map borders - if a <> 0 then - begin - // left border - iy:= (leftX + mapBorderMargin - mp.x) * b div a + mp.y; - d:= DistanceI(mp.x - leftX - mapBorderMargin, mp.y - iy).Round; - t1:= a * (mp.x - mapBorderMargin) + b * (mp.y - iy); - if t1 > 0 then distL:= d else distR:= d; - - // right border - iy:= (rightX - mapBorderMargin - mp.x) * b div a + mp.y; - d:= DistanceI(mp.x - rightX + mapBorderMargin, mp.y - iy).Round; - if t1 > 0 then distR:= d else distL:= d; - end else - begin - distL:= LAND_WIDTH + LAND_HEIGHT; - distR:= distL; - end; - - if b <> 0 then - begin - // top border - ix:= (topY + mapBorderMargin - mp.y) * a div b + mp.x; - d:= DistanceI(mp.y - topY - mapBorderMargin, mp.x - ix).Round; - t2:= b * (mp.y - mapBorderMargin) + a * (mp.x - ix); - if t2 > 0 then distL:= min(d, distL) else distR:= min(d, distR); - - // bottom border - ix:= (LAND_HEIGHT - mapBorderMargin - mp.y) * a div b + mp.x; - d:= DistanceI(mp.y - LAND_HEIGHT + mapBorderMargin, mp.x - ix).Round; - if t2 > 0 then distR:= min(d, distR) else distL:= min(d, distL); - end; - - // now go through all other segments - fp:= pa.ar[0]; - for i:= 0 to LongInt(pa.Count) - 2 do - if pa.ar[i].x = NTPX then - fp:= pa.ar[i + 1] - else if (i <> si) then - begin - p4:= pa.ar[i + 1]; - if p4.x = NTPX then - p4:= fp; - - // check if it intersects - t1:= (mp.x - pa.ar[i].x) * b - a * (mp.y - pa.ar[i].y); - t2:= (mp.x - p4.x) * b - a * (mp.y - p4.y); - - if (t1 > 0) <> (t2 > 0) then // yes it does, hard arith follows - begin - p:= p4.x - pa.ar[i].x; - q:= p4.y - pa.ar[i].y; - aqpb:= a * q - p * b; - - if (aqpb <> 0) then - begin - // (ix; iy) is intersection point - iy:= (((Int64(pa.ar[i].x) - mp.x) * b + Int64(mp.y) * a) * q - Int64(pa.ar[i].y) * p * b) div aqpb; - if abs(b) > abs(q) then - ix:= (iy - mp.y) * a div b + mp.x - else - ix:= (iy - pa.ar[i].y) * p div q + pa.ar[i].x; - - d:= DistanceI(mp.y - iy, mp.x - ix).Round; - t1:= b * (mp.y - iy) + a * (mp.x - ix); - if t1 > 0 then distL:= min(d, distL) else distR:= min(d, distR); - end; - end; - end; - - // go through all points, including fill points - for i:= 0 to Pred(LongInt(pa.Count + fillPointsCount)) do - // if this point isn't on current segment - if (si <> i) and (i <> si + 1) and (pa.ar[i].x <> NTPX) then - begin - // also check intersection with rays through pa.ar[i] if this point is good - t1:= (p1.x - pa.ar[i].x) * b - a * (p1.y - pa.ar[i].y); - t2:= (p2.x - pa.ar[i].x) * b - a * (p2.y - pa.ar[i].y); - if (t1 > 0) <> (t2 > 0) then - begin - // ray from p1 - p:= pa.ar[i].x - p1.x; - q:= pa.ar[i].y - p1.y; - aqpb:= a * q - p * b; - - if (aqpb <> 0) then - begin - // (ix; iy) is intersection point - iy:= (((Int64(p1.x) - mp.x) * b + Int64(mp.y) * a) * q - Int64(p1.y) * p * b) div aqpb; - if abs(b) > abs(q) then - ix:= (iy - mp.y) * a div b + mp.x - else - ix:= (iy - p1.y) * p div q + p1.x; - - d:= DistanceI(mp.y - iy, mp.x - ix).Round; - t1:= b * (mp.y - iy) + a * (mp.x - ix); - if t1 > 0 then distL:= min(d, distL) else distR:= min(d, distR); - end; - - // and ray from p2 - p:= pa.ar[i].x - p2.x; - q:= pa.ar[i].y - p2.y; - aqpb:= a * q - p * b; - - if (aqpb <> 0) then - begin - // (ix; iy) is intersection point - iy:= (((Int64(p2.x) - mp.x) * b + Int64(mp.y) * a) * q - Int64(p2.y) * p * b) div aqpb; - if abs(b) > abs(q) then - ix:= (iy - mp.y) * a div b + mp.x - else - ix:= (iy - p2.y) * p div q + p2.x; - - d:= DistanceI(mp.y - iy, mp.x - ix).Round; - t2:= b * (mp.y - iy) + a * (mp.x - ix); - if t2 > 0 then distL:= min(d, distL) else distR:= min(d, distR); - end; - end; - end; - - // don't move new point for more than length of initial segment - // adjust/parametrize for more flat surfaces (try values 3/4, 1/2 of dab, or even 1/4) - d:= dab * 100 div dabDiv; - //d:= dab * (1 + abs(cFeatureSize - 8)) div 6; - //d:= dab * (14 + cFeatureSize) div 20; - if distL > d then distL:= d; - if distR > d then distR:= d; - - if distR + distL < minDistance * 2 + 10 then - begin - // limits are too narrow, just divide - newPoint.x:= mp.x; - newPoint.y:= mp.y; - end - else - begin - // select distance within [-distL; distR] - d:= -distL + minDistance + LongInt(GetRandom(distR + distL - minDistance * 2)); - //d:= distR - minDistance; - //d:= - distL + minDistance; - - // calculate new point - newPoint.x:= mp.x + a * d div dab; - newPoint.y:= mp.y + b * d div dab; - end; -end; - -procedure DivideEdges(fillPointsCount: LongWord; var pa: TPixAr); -var i, t: LongInt; - newPoint: TPoint; -begin - newPoint.x:= 0; - newPoint.y:= 0; - i:= 0; - - while i < LongInt(pa.Count) - 1 do - begin - FindPoint(i, fillPointsCount, newPoint, pa); - - if (newPoint.x <> pa.ar[i].x) or (newPoint.y <> pa.ar[i].y) then - begin - // point found, free a slot for it in array, don't forget to move appended fill points - for t:= pa.Count + fillPointsCount downto i + 2 do - pa.ar[t]:= pa.ar[t - 1]; - inc(pa.Count); - pa.ar[i + 1]:= newPoint; - inc(i) - end; - inc(i) - end; -end; - -procedure Distort2(var Template: TEdgeTemplate; fps: PPointArray; var pa: TPixAr); -var i: Longword; -begin - // append fill points to ensure distortion won't move them to other side of segment - for i:= 0 to pred(Template.FillPointsCount) do - begin - pa.ar[pa.Count + i].x:= fps^[i].x; - pa.ar[pa.Count + i].y:= fps^[i].y; - end; - - // divide while it divides - repeat - i:= pa.Count; - DivideEdges(Template.FillPointsCount, pa) - until i = pa.Count; - -{$IFDEF IPHONEOS} - if GameType <> gmtLandPreview then -{$ENDIF} - // make it smooth - BezierizeEdge(pa, _0_2); -end; - - -procedure GenTemplated(var Template: TEdgeTemplate); -var pa: TPixAr; - i: Longword; - y, x: Longword; - fps: TPointArray; -begin - fps:=Template.FillPoints^; - ResizeLand(Template.TemplateWidth, Template.TemplateHeight); - for y:= 0 to LAND_HEIGHT - 1 do - for x:= 0 to LAND_WIDTH - 1 do - Land[y, x]:= lfBasic; - - minDistance:= sqr(cFeatureSize) div 8 + 10; - //dabDiv:= getRandom(41)+60; - //dabDiv:= getRandom(31)+70; - dabDiv:= getRandom(21)+100; - MaxHedgehogs:= Template.MaxHedgehogs; - hasGirders:= Template.hasGirders; - playHeight:= Template.TemplateHeight; - playWidth:= Template.TemplateWidth; - leftX:= (LAND_WIDTH - playWidth) div 2; - rightX:= Pred(leftX + playWidth); - topY:= LAND_HEIGHT - playHeight; - - {$HINTS OFF} - SetPoints(Template, pa, @fps); - {$HINTS ON} - - Distort2(Template, @fps, pa); - - DrawEdge(pa, 0); - - with Template do - for i:= 0 to pred(FillPointsCount) do - with fps[i] do - FillLand(x, y, 0, 0); - - DrawEdge(pa, lfBasic); - - // HACK: force to only cavern even if a cavern map is invertable if cTemplateFilter = 4 ? - if (cTemplateFilter = 4) - or (Template.canInvert and (getrandom(2) = 0)) - or (not Template.canInvert and Template.isNegative) then - begin - hasBorder:= true; - for y:= 0 to LAND_HEIGHT - 1 do - for x:= 0 to LAND_WIDTH - 1 do - if (y < LongWord(topY)) or (x < LongWord(leftX)) or (x > LongWord(rightX)) then - Land[y, x]:= 0 - else - begin - if Land[y, x] = 0 then - Land[y, x]:= lfBasic - else if Land[y, x] = lfBasic then - Land[y, x]:= 0; - end; - end; -end; - - -end. diff -r 2b4f361e3891 -r 629d5123a979 hedgewars/uLandGraphics.pas --- a/hedgewars/uLandGraphics.pas Sat Sep 28 22:27:13 2024 +0200 +++ b/hedgewars/uLandGraphics.pas Tue Dec 31 15:19:43 2024 +0100 @@ -47,19 +47,19 @@ function DrawThickLine(X1, Y1, X2, Y2, radius: LongInt; color: Longword): Longword; procedure DumpLandToLog(x, y, r: LongInt); procedure DrawIceBreak(x, y, iceRadius, iceHeight: Longint); -function TryPlaceOnLandSimple(cpX, cpY: LongInt; Obj: TSprite; Frame: LongInt; doPlace, indestructible: boolean): boolean; inline; -function TryPlaceOnLand(cpX, cpY: LongInt; Obj: TSprite; Frame: LongInt; doPlace: boolean; LandFlags: Word): boolean; inline; -function ForcePlaceOnLand(cpX, cpY: LongInt; Obj: TSprite; Frame: LongInt; LandFlags: Word; Tint: LongWord; Behind, flipHoriz, flipVert: boolean): boolean; inline; +function TryPlaceOnLandSimple(cpX, cpY: LongInt; Obj: TSprite; Frame: LongInt; doPlace, indestructible: boolean): boolean; +function TryPlaceOnLand(cpX, cpY: LongInt; Obj: TSprite; Frame: LongInt; doPlace: boolean; LandFlags: Word): boolean; +function ForcePlaceOnLand(cpX, cpY: LongInt; Obj: TSprite; Frame: LongInt; LandFlags: Word; Tint: LongWord; Behind, flipHoriz, flipVert: boolean): boolean; function TryPlaceOnLand(cpX, cpY: LongInt; Obj: TSprite; Frame: LongInt; doPlace, outOfMap, force, behind, flipHoriz, flipVert: boolean; LandFlags: Word; Tint: LongWord): boolean; procedure EraseLandRectRaw(X, Y, width, height: LongWord); procedure EraseLand(cpX, cpY: LongInt; Obj: TSprite; Frame: LongInt; LandFlags: Word; eraseOnLFMatch, onlyEraseLF, flipHoriz, flipVert: boolean); function GetPlaceCollisionTex(cpX, cpY: LongInt; Obj: TSprite; Frame: LongInt): PTexture; implementation -uses SDLh, uLandTexture, uTextures, uVariables, uUtils, uDebug, uScript; +uses SDLh, uLandTexture, uTextures, uVariables, uUtils, uDebug, uScript, uLandUtils; -procedure calculatePixelsCoordinates(landX, landY: Longint; var pixelX, pixelY: Longint); inline; +procedure calculatePixelsCoordinates(landX, landY: Longint; var pixelX, pixelY: Longint); begin if (cReducedQuality and rqBlurryLand) = 0 then begin @@ -73,33 +73,33 @@ end; end; -function drawPixelBG(landX, landY, pixelX, pixelY: Longint): Longword; inline; +function drawPixelBG(landX, landY, pixelX, pixelY: Longint): Longword; begin drawPixelBG := 0; -if (Land[LandY, landX] and lfIndestructible) = 0 then +if (LandGet(LandY, landX) and lfIndestructible) = 0 then begin - if ((Land[landY, landX] and lfBasic) <> 0) and (((LandPixels[pixelY, pixelX] and AMask) shr AShift) = 255) and (not disableLandBack) then + if ((LandGet(landY, landX) and lfBasic) <> 0) and (((LandPixelGet(pixelY, pixelX) and AMask) shr AShift) = 255) and (not disableLandBack) then begin - LandPixels[pixelY, pixelX]:= LandBackPixel(landX, landY); + LandPixelSet(pixelY, pixelX, LandBackPixel(landX, landY)); inc(drawPixelBG); end - else if ((Land[landY, landX] and lfObject) <> 0) or (((LandPixels[pixelY, pixelX] and AMask) shr AShift) < 255) then - LandPixels[pixelY, pixelX]:= ExplosionBorderColorNoA + else if ((LandGet(landY, landX) and lfObject) <> 0) or (((LandPixelGet(pixelY, pixelX) and AMask) shr AShift) < 255) then + LandPixelSet(pixelY, pixelX, ExplosionBorderColorNoA) end; end; -procedure drawPixelEBC(landX, landY, pixelX, pixelY: Longint); inline; +procedure drawPixelEBC(landX, landY, pixelX, pixelY: Longint); begin -if (Land[landY, landX] and lfIndestructible = 0) and - (((Land[landY, landX] and lfBasic) <> 0) or ((Land[landY, landX] and lfObject) <> 0)) then +if (LandGet(landY, landX) and lfIndestructible = 0) and + (((LandGet(landY, landX) and lfBasic) <> 0) or ((LandGet(landY, landX) and lfObject) <> 0)) then begin - LandPixels[pixelY, pixelX]:= ExplosionBorderColor; - Land[landY, landX]:= (Land[landY, landX] or lfDamaged) and (not lfIce); + LandPixelSet(pixelY, pixelX, ExplosionBorderColor); + LandSet(landY, landX, (LandGet(landY, landX) or lfDamaged) and (not lfIce)); LandDirty[landY div 32, landX div 32]:= 1; end; end; -function isLandscapeEdge(weight:Longint):boolean; inline; +function isLandscapeEdge(weight:Longint):boolean; begin isLandscapeEdge := (weight < 8) and (weight >= 2); end; @@ -118,7 +118,7 @@ (j > LAND_HEIGHT -1) then exit(9); - if Land[j, i] and lfLandMask and (not lfIce) = 0 then + if LandGet(j, i) and lfLandMask and (not lfIce) = 0 then inc(r) end; @@ -126,7 +126,7 @@ end; -procedure fillPixelFromIceSprite(pixelX, pixelY:Longint); inline; +procedure fillPixelFromIceSprite(pixelX, pixelY:Longint); var iceSurface: PSDL_Surface; icePixels: PLongwordArray; @@ -136,7 +136,7 @@ // So. 3 parameters here. Ice colour, Ice opacity, and a bias on the greyscaled pixel towards lightness iceSurface:= SpritesData[sprIceTexture].Surface; icePixels := iceSurface^.pixels; - w:= LandPixels[pixelY, pixelX]; + w:= LandPixelGet(pixelY, pixelX); if w > 0 then begin w:= round(((w shr RShift and $FF) * RGB_LUMINANCE_RED + @@ -144,37 +144,37 @@ (w shr GShift and $FF) * RGB_LUMINANCE_BLUE)); if w < 128 then w:= w+128; if w > 255 then w:= 255; - w:= (w shl RShift) or (w shl BShift) or (w shl GShift) or (LandPixels[pixelY, pixelX] and AMask); - LandPixels[pixelY, pixelX]:= addBgColor(w, IceColor); - LandPixels[pixelY, pixelX]:= addBgColor(LandPixels[pixelY, pixelX], icePixels^[iceSurface^.w * (pixelY mod iceSurface^.h) + (pixelX mod iceSurface^.w)]) + w:= (w shl RShift) or (w shl BShift) or (w shl GShift) or (LandPixelGet(pixelY, pixelX) and AMask); + LandPixelSet(pixelY, pixelX, addBgColor(w, IceColor)); + LandPixelSet(pixelY, pixelX, addBgColor(LandPixelGet(pixelY, pixelX), icePixels^[iceSurface^.w * (pixelY mod iceSurface^.h) + (pixelX mod iceSurface^.w)])) end else begin - LandPixels[pixelY, pixelX]:= IceColor and (not AMask) or $E8 shl AShift; - LandPixels[pixelY, pixelX]:= addBgColor(LandPixels[pixelY, pixelX], icePixels^[iceSurface^.w * (pixelY mod iceSurface^.h) + (pixelX mod iceSurface^.w)]); + LandPixelSet(pixelY, pixelX, IceColor and (not AMask) or $E8 shl AShift); + LandPixelSet(pixelY, pixelX, addBgColor(LandPixelGet(pixelY, pixelX), icePixels^[iceSurface^.w * (pixelY mod iceSurface^.h) + (pixelX mod iceSurface^.w)])); // silly workaround to avoid having to make background erasure a tadb it smarter about sea ice - if LandPixels[pixelY, pixelX] and AMask shr AShift = 255 then - LandPixels[pixelY, pixelX]:= LandPixels[pixelY, pixelX] and (not AMask) or 254 shl AShift; + if LandPixelGet(pixelY, pixelX) and AMask shr AShift = 255 then + LandPixelSet(pixelY, pixelX, LandPixelGet(pixelY, pixelX) and (not AMask) or 254 shl AShift); end; end; -procedure DrawPixelIce(landX, landY, pixelX, pixelY: Longint); inline; +procedure DrawPixelIce(landX, landY, pixelX, pixelY: Longint); begin -if ((Land[landY, landX] and lfIce) <> 0) then exit; +if ((LandGet(landY, landX) and lfIce) <> 0) then exit; if (pixelX < LeftX) or (pixelX > RightX) or (pixelY < TopY) then exit; if isLandscapeEdge(getPixelWeight(landX, landY)) then begin - if (LandPixels[pixelY, pixelX] and AMask < 255) and (LandPixels[pixelY, pixelX] and AMask > 0) then - LandPixels[pixelY, pixelX] := (IceEdgeColor and (not AMask)) or (LandPixels[pixelY, pixelX] and AMask) - else if (LandPixels[pixelY, pixelX] and AMask < 255) or (Land[landY, landX] > 255) then - LandPixels[pixelY, pixelX] := IceEdgeColor + if (LandPixelGet(pixelY, pixelX) and AMask < 255) and (LandPixelGet(pixelY, pixelX) and AMask > 0) then + LandPixelSet(pixelY, pixelX, (IceEdgeColor and (not AMask)) or (LandPixelGet(pixelY, pixelX) and AMask)) + else if (LandPixelGet(pixelY, pixelX) and AMask < 255) or (LandGet(landY, landX) > 255) then + LandPixelSet(pixelY, pixelX, IceEdgeColor) end -else if Land[landY, landX] > 255 then +else if LandGet(landY, landX) > 255 then begin fillPixelFromIceSprite(pixelX, pixelY); end; -if Land[landY, landX] > 255 then Land[landY, landX] := Land[landY, landX] or lfIce and (not lfDamaged); +if LandGet(landY, landX) > 255 then LandSet(landY, landX, LandGet(landY, landX) or lfIce and (not lfDamaged)); end; @@ -202,8 +202,8 @@ for i:= fromPix to toPix do begin calculatePixelsCoordinates(i, y, px, py); - if ((Land[y, i] and lfIndestructible) = 0) and (not disableLandBack or (Land[y, i] > 255)) then - LandPixels[py, px]:= ExplosionBorderColorNoA; + if ((LandGet(y, i) and lfIndestructible) = 0) and (not disableLandBack or (LandGet(y, i) > 255)) then + LandPixelSet(py, px, ExplosionBorderColorNoA); end; icePixel: for i:= fromPix to toPix do @@ -214,41 +214,41 @@ addNotHHObj: for i:= fromPix to toPix do begin - if Land[y, i] and lfNotHHObjMask shr lfNotHHObjShift < lfNotHHObjSize then - Land[y, i]:= (Land[y, i] and (not lfNotHHObjMask)) or ((Land[y, i] and lfNotHHObjMask shr lfNotHHObjShift + 1) shl lfNotHHObjShift); + if LandGet(y, i) and lfNotHHObjMask shr lfNotHHObjShift < lfNotHHObjSize then + LandSet(y, i, (LandGet(y, i) and (not lfNotHHObjMask)) or ((LandGet(y, i) and lfNotHHObjMask shr lfNotHHObjShift + 1) shl lfNotHHObjShift)); end; removeNotHHObj: for i:= fromPix to toPix do begin - if Land[y, i] and lfNotHHObjMask <> 0 then - Land[y, i]:= (Land[y, i] and (not lfNotHHObjMask)) or ((Land[y, i] and lfNotHHObjMask shr lfNotHHObjShift - 1) shl lfNotHHObjShift); + if LandGet(y, i) and lfNotHHObjMask <> 0 then + LandSet(y, i, (LandGet(y, i) and (not lfNotHHObjMask)) or ((LandGet(y, i) and lfNotHHObjMask shr lfNotHHObjShift - 1) shl lfNotHHObjShift)); end; addHH: for i:= fromPix to toPix do begin - if Land[y, i] and lfHHMask < lfHHMask then - Land[y, i]:= Land[y, i] + 1 + if LandGet(y, i) and lfHHMask < lfHHMask then + LandSet(y, i, LandGet(y, i) + 1) end; removeHH: for i:= fromPix to toPix do begin - if Land[y, i] and lfHHMask > 0 then - Land[y, i]:= Land[y, i] - 1; + if LandGet(y, i) and lfHHMask > 0 then + LandSet(y, i, LandGet(y, i) - 1); end; setCurrentHog: for i:= fromPix to toPix do begin - Land[y, i]:= Land[y, i] or lfCurHogCrate + LandSet(y, i, LandGet(y, i) or lfCurHogCrate) end; removeCurrentHog: for i:= fromPix to toPix do begin - Land[y, i]:= Land[y, i] and lfNotCurHogCrate; + LandSet(y, i, LandGet(y, i) and lfNotCurHogCrate); end; end; end; -function FillLandCircleSegmentFT(x, y, dx, dy: LongInt; fill : fillType): Longword; inline; +function FillLandCircleSegmentFT(x, y, dx, dy: LongInt; fill : fillType): Longword; begin FillLandCircleSegmentFT := 0; if ((y + dy) and LAND_HEIGHT_MASK) = 0 then @@ -261,7 +261,7 @@ inc(FillLandCircleSegmentFT, FillLandCircleLineFT(y - dx, Max(x - dy, 0), Min(x + dy, LAND_WIDTH - 1), fill)); end; -function FillRoundInLandFT(X, Y, Radius: LongInt; fill: fillType): Longword; inline; +function FillRoundInLandFT(X, Y, Radius: LongInt; fill: fillType): Longword; var dx, dy, d: LongInt; begin dx:= 0; @@ -323,31 +323,31 @@ if ((y + dy) and LAND_HEIGHT_MASK) = 0 then for i:= Max(x - dx, 0) to Min(x + dx, LAND_WIDTH - 1) do - if (Land[y + dy, i] and lfIndestructible) = 0 then + if (LandGet(y + dy, i) and lfIndestructible) = 0 then begin - if Land[y + dy, i] <> Value then inc(FillCircleLines); - Land[y + dy, i]:= Value; + if LandGet(y + dy, i) <> Value then inc(FillCircleLines); + LandSet(y + dy, i, Value); end; if ((y - dy) and LAND_HEIGHT_MASK) = 0 then for i:= Max(x - dx, 0) to Min(x + dx, LAND_WIDTH - 1) do - if (Land[y - dy, i] and lfIndestructible) = 0 then + if (LandGet(y - dy, i) and lfIndestructible) = 0 then begin - if Land[y - dy, i] <> Value then inc(FillCircleLines); - Land[y - dy, i]:= Value; + if LandGet(y - dy, i) <> Value then inc(FillCircleLines); + LandSet(y - dy, i, Value); end; if ((y + dx) and LAND_HEIGHT_MASK) = 0 then for i:= Max(x - dy, 0) to Min(x + dy, LAND_WIDTH - 1) do - if (Land[y + dx, i] and lfIndestructible) = 0 then + if (LandGet(y + dx, i) and lfIndestructible) = 0 then begin - if Land[y + dx, i] <> Value then inc(FillCircleLines); - Land[y + dx, i]:= Value; + if LandGet(y + dx, i) <> Value then inc(FillCircleLines); + LandSet(y + dx, i, Value); end; if ((y - dx) and LAND_HEIGHT_MASK) = 0 then for i:= Max(x - dy, 0) to Min(x + dy, LAND_WIDTH - 1) do - if (Land[y - dx, i] and lfIndestructible) = 0 then + if (LandGet(y - dx, i) and lfIndestructible) = 0 then begin - if Land[y - dx, i] <> Value then inc(FillCircleLines); - Land[y - dx, i]:= Value; + if LandGet(y - dx, i) <> Value then inc(FillCircleLines); + LandSet(y - dx, i, Value); end; end; @@ -435,9 +435,9 @@ begin for j := iceT to iceB do begin - if Land[j, i] = 0 then + if LandGet(j, i) = 0 then begin - Land[j, i] := lfIce; + LandSet(j, i, lfIce); if (cReducedQuality and rqBlurryLand) = 0 then fillPixelFromIceSprite(i, j) else @@ -478,7 +478,7 @@ for ty:= Max(y - Radius, 0) to Min(y + Radius, TopY) do for tx:= Max(LeftX, ar^[i].Left - Radius) to Min(RightX, ar^[i].Right + Radius) do begin - if (Land[ty, tx] and lfIndestructible) = 0 then + if (LandGet(ty, tx) and lfIndestructible) = 0 then begin if (cReducedQuality and rqBlurryLand) = 0 then begin @@ -488,10 +488,10 @@ begin by:= ty div 2; bx:= tx div 2; end; - if ((Land[ty, tx] and lfBasic) <> 0) and (((LandPixels[by,bx] and AMask) shr AShift) = 255) and (not disableLandBack) then - LandPixels[by, bx]:= LandBackPixel(tx, ty) - else if ((Land[ty, tx] and lfObject) <> 0) or (((LandPixels[by,bx] and AMask) shr AShift) < 255) then - LandPixels[by, bx]:= LandPixels[by, bx] and (not AMASK) + if ((LandGet(ty, tx) and lfBasic) <> 0) and (((LandPixelGet(by,bx) and AMask) shr AShift) = 255) and (not disableLandBack) then + LandPixelSet(by, bx, LandBackPixel(tx, ty)) + else if ((LandGet(ty, tx) and lfObject) <> 0) or (((LandPixelGet(by,bx) and AMask) shr AShift) < 255) then + LandPixelSet(by, bx, LandPixelGet(by, bx) and (not AMASK)) end end; inc(y, dY) @@ -504,14 +504,14 @@ begin for ty:= Max(y - Radius, 0) to Min(y + Radius, TopY) do for tx:= Max(LeftX, ar^[i].Left - Radius) to Min(RightX, ar^[i].Right + Radius) do - if ((Land[ty, tx] and lfBasic) <> 0) or ((Land[ty, tx] and lfObject) <> 0) then + if ((LandGet(ty, tx) and lfBasic) <> 0) or ((LandGet(ty, tx) and lfObject) <> 0) then begin if (cReducedQuality and rqBlurryLand) = 0 then - LandPixels[ty, tx]:= ExplosionBorderColor + LandPixelSet(ty, tx, ExplosionBorderColor) else - LandPixels[ty div 2, tx div 2]:= ExplosionBorderColor; + LandPixelSet(ty div 2, tx div 2, ExplosionBorderColor); - Land[ty, tx]:= (Land[ty, tx] or lfDamaged) and (not lfIce); + LandSet(ty, tx, (LandGet(ty, tx) or lfDamaged) and (not lfIce)); LandDirty[ty div 32, tx div 32]:= 1; end; inc(y, dY) @@ -533,16 +533,16 @@ Y:= Y + dY; tx:= hwRound(X); ty:= hwRound(Y); - if ((ty and LAND_HEIGHT_MASK) = 0) and ((tx and LAND_WIDTH_MASK) = 0) and (((Land[ty, tx] and lfBasic) <> 0) - or ((Land[ty, tx] and lfObject) <> 0)) then + if ((ty and LAND_HEIGHT_MASK) = 0) and ((tx and LAND_WIDTH_MASK) = 0) and (((LandGet(ty, tx) and lfBasic) <> 0) + or ((LandGet(ty, tx) and lfObject) <> 0)) then begin - Land[ty, tx]:= (Land[ty, tx] or lfDamaged) and (not lfIce); + LandSet(ty, tx, (LandGet(ty, tx) or lfDamaged) and (not lfIce)); if despeckle then LandDirty[ty div 32, tx div 32]:= 1; if (cReducedQuality and rqBlurryLand) = 0 then - LandPixels[ty, tx]:= ExplosionBorderColor + LandPixelSet(ty, tx, ExplosionBorderColor) else - LandPixels[ty div 2, tx div 2]:= ExplosionBorderColor + LandPixelSet(ty div 2, tx div 2, ExplosionBorderColor) end end; end; @@ -581,18 +581,18 @@ ty:= hwRound(Y); if ((ty and LAND_HEIGHT_MASK) = 0) and ((tx and LAND_WIDTH_MASK) = 0) - and (((Land[ty, tx] and lfBasic) <> 0) or ((Land[ty, tx] and lfObject) <> 0)) then + and (((LandGet(ty, tx) and lfBasic) <> 0) or ((LandGet(ty, tx) and lfObject) <> 0)) then begin - Land[ty, tx]:= Land[ty, tx] and (not lfIce); + LandSet(ty, tx, LandGet(ty, tx) and (not lfIce)); if despeckle then begin - Land[ty, tx]:= Land[ty, tx] or lfDamaged; + LandSet(ty, tx, LandGet(ty, tx) or lfDamaged); LandDirty[ty div 32, tx div 32]:= 1 end; if (cReducedQuality and rqBlurryLand) = 0 then - LandPixels[ty, tx]:= ExplosionBorderColor + LandPixelSet(ty, tx, ExplosionBorderColor) else - LandPixels[ty div 2, tx div 2]:= ExplosionBorderColor + LandPixelSet(ty div 2, tx div 2, ExplosionBorderColor) end end; nx:= nx - dY; @@ -612,7 +612,7 @@ Y:= Y + dY; tx:= hwRound(X); ty:= hwRound(Y); - if ((ty and LAND_HEIGHT_MASK) = 0) and ((tx and LAND_WIDTH_MASK) = 0) and ((Land[ty, tx] and lfIndestructible) = 0) then + if ((ty and LAND_HEIGHT_MASK) = 0) and ((tx and LAND_WIDTH_MASK) = 0) and ((LandGet(ty, tx) and lfIndestructible) = 0) then begin if (cReducedQuality and rqBlurryLand) = 0 then begin @@ -622,11 +622,11 @@ begin by:= ty div 2; bx:= tx div 2; end; - if ((Land[ty, tx] and lfBasic) <> 0) and (((LandPixels[by,bx] and AMask) shr AShift) = 255) and (not disableLandBack) then - LandPixels[by, bx]:= LandBackPixel(tx, ty) - else if ((Land[ty, tx] and lfObject) <> 0) or (((LandPixels[by,bx] and AMask) shr AShift) < 255) then - LandPixels[by, bx]:= LandPixels[by, bx] and (not AMASK); - Land[ty, tx]:= 0; + if ((LandGet(ty, tx) and lfBasic) <> 0) and (((LandPixelGet(by,bx) and AMask) shr AShift) = 255) and (not disableLandBack) then + LandPixelSet(by, bx, LandBackPixel(tx, ty)) + else if ((LandGet(ty, tx) and lfObject) <> 0) or (((LandPixelGet(by,bx) and AMask) shr AShift) < 255) then + LandPixelSet(by, bx, LandPixelGet(by, bx) and (not AMASK)); + LandSet(ty, tx, 0); end end; DrawExplosionBorder(X, Y, dx, dy, despeckle); @@ -644,16 +644,16 @@ Y:= Y + dY; tx:= hwRound(X); ty:= hwRound(Y); - if ((ty and LAND_HEIGHT_MASK) = 0) and ((tx and LAND_WIDTH_MASK) = 0) and (((Land[ty, tx] and lfBasic) <> 0) - or ((Land[ty, tx] and lfObject) <> 0)) then + if ((ty and LAND_HEIGHT_MASK) = 0) and ((tx and LAND_WIDTH_MASK) = 0) and (((LandGet(ty, tx) and lfBasic) <> 0) + or ((LandGet(ty, tx) and lfObject) <> 0)) then begin - Land[ty, tx]:= (Land[ty, tx] or lfDamaged) and (not lfIce); + LandSet(ty, tx, (LandGet(ty, tx) or lfDamaged) and (not lfIce)); if despeckle then LandDirty[ty div 32, tx div 32]:= 1; if (cReducedQuality and rqBlurryLand) = 0 then - LandPixels[ty, tx]:= ExplosionBorderColor + LandPixelSet(ty, tx, ExplosionBorderColor) else - LandPixels[ty div 2, tx div 2]:= ExplosionBorderColor + LandPixelSet(ty div 2, tx div 2, ExplosionBorderColor) end end; nx:= nx - dY; @@ -692,7 +692,7 @@ end; end; -function TryPlaceOnLandSimple(cpX, cpY: LongInt; Obj: TSprite; Frame: LongInt; doPlace, indestructible: boolean): boolean; inline; +function TryPlaceOnLandSimple(cpX, cpY: LongInt; Obj: TSprite; Frame: LongInt; doPlace, indestructible: boolean): boolean; var lf: Word; begin if indestructible then @@ -702,12 +702,12 @@ TryPlaceOnLandSimple:= TryPlaceOnLand(cpX, cpY, Obj, Frame, doPlace, false, false, false, false, false, lf, $FFFFFFFF); end; -function TryPlaceOnLand(cpX, cpY: LongInt; Obj: TSprite; Frame: LongInt; doPlace: boolean; LandFlags: Word): boolean; inline; +function TryPlaceOnLand(cpX, cpY: LongInt; Obj: TSprite; Frame: LongInt; doPlace: boolean; LandFlags: Word): boolean; begin TryPlaceOnLand:= TryPlaceOnLand(cpX, cpY, Obj, Frame, doPlace, false, false, false, false, false, LandFlags, $FFFFFFFF); end; -function ForcePlaceOnLand(cpX, cpY: LongInt; Obj: TSprite; Frame: LongInt; LandFlags: Word; Tint: LongWord; Behind, flipHoriz, flipVert: boolean): boolean; inline; +function ForcePlaceOnLand(cpX, cpY: LongInt; Obj: TSprite; Frame: LongInt; LandFlags: Word; Tint: LongWord; Behind, flipHoriz, flipVert: boolean): boolean; begin ForcePlaceOnLand:= TryPlaceOnLand(cpX, cpY, Obj, Frame, true, false, true, behind, flipHoriz, flipVert, LandFlags, Tint) end; @@ -752,12 +752,12 @@ if (outOfMap and ((cpY + y) < LAND_HEIGHT) and ((cpY + y) >= 0) and ((cpX + x) < LAND_WIDTH) and ((cpX + x) >= 0) and - ((not force) and (Land[cpY + y, cpX + x] <> 0))) or + ((not force) and (LandGet(cpY + y, cpX + x) <> 0))) or (not outOfMap and (((cpY + y) <= topY) or ((cpY + y) >= LAND_HEIGHT) or ((cpX + x) <= leftX) or ((cpX + x) >= rightX) or - ((not force) and (Land[cpY + y, cpX + x] <> 0)))) then + ((not force) and (LandGet(cpY + y, cpX + x) <> 0)))) then begin if SDL_MustLock(Image) then SDL_UnlockSurface(Image); @@ -793,28 +793,28 @@ gX:= (cpX + x) div 2; gY:= (cpY + y) div 2; end; - if (not behind) or (Land[cpY + y, cpX + x] and lfLandMask = 0) then + if (not behind) or (LandGet(cpY + y, cpX + x) and lfLandMask = 0) then begin - if (LandFlags and lfBasic <> 0) or - ((LandPixels[gY, gX] and AMask shr AShift > 128) and // This test assumes lfBasic and lfObject differ only graphically + if (LandFlags and lfBasic <> 0) or + ((LandPixelGet(gY, gX) and AMask shr AShift > 128) and // This test assumes lfBasic and lfObject differ only graphically (LandFlags and (lfObject or lfIce) = 0)) then - Land[cpY + y, cpX + x]:= lfBasic or LandFlags + LandSet(cpY + y, cpX + x, lfBasic or LandFlags) else if (LandFlags and lfIce = 0) then - Land[cpY + y, cpX + x]:= lfObject or LandFlags - else Land[cpY + y, cpX + x]:= LandFlags + LandSet(cpY + y, cpX + x, lfObject or LandFlags) + else LandSet(cpY + y, cpX + x, LandFlags) end; - if (not behind) or (LandPixels[gY, gX] = 0) then + if (not behind) or (LandPixelGet(gY, gX) = 0) then begin if tint = $FFFFFFFF then - LandPixels[gY, gX]:= PLongword(@(p^[x * 4]))^ - else + LandPixelSet(gY, gX, PLongword(@(p^[x * 4]))^) + else begin pixel:= PLongword(@(p^[x * 4]))^; - LandPixels[gY, gX]:= + LandPixelSet(gY, gX, ceil((pixel shr RShift and $FF) * ((tint shr 24) / 255)) shl RShift or ceil((pixel shr GShift and $FF) * ((tint shr 16 and $ff) / 255)) shl GShift or ceil((pixel shr BShift and $FF) * ((tint shr 8 and $ff) / 255)) shl BShift or - ceil((pixel shr AShift and $FF) * ((tint and $ff) / 255)) shl AShift; + ceil((pixel shr AShift and $FF) * ((tint and $ff) / 255)) shl AShift); end end end; @@ -847,8 +847,8 @@ for ty:= 0 to height - 1 do for tx:= 0 to width - 1 do begin - LandPixels[ty, tx]:= 0; - Land[Y + ty, X + tx]:= 0; + LandPixelSet(ty, tx, 0); + LandSet(Y + ty, X + tx, 0); end; end; @@ -913,15 +913,15 @@ gX:= (cpX + x) div 2; gY:= (cpY + y) div 2; end; - if (not eraseOnLFMatch or (Land[cpY + y, cpX + x] and LandFlags <> 0)) and + if (not eraseOnLFMatch or (LandGet(cpY + y, cpX + x) and LandFlags <> 0)) and ((PLongword(@(p^[x * 4]))^) and AMask <> 0) then begin if not onlyEraseLF then begin - LandPixels[gY, gX]:= 0; - Land[cpY + y, cpX + x]:= 0 + LandPixelSet(gY, gX, 0); + LandSet(cpY + y, cpX + x, 0) end - else Land[cpY + y, cpX + x]:= Land[cpY + y, cpX + x] and (not LandFlags) + else LandSet(cpY + y, cpX + x, LandGet(cpY + y, cpX + x) and (not LandFlags)) end end; p:= PByteArray(@(p^[Image^.pitch])); @@ -990,7 +990,7 @@ for x:= 0 to Pred(w) do if ((p^[x] and AMask) <> 0) and (((cpY + y) < topY) or ((cpY + y) >= LAND_HEIGHT) or - ((cpX + x) < leftX) or ((cpX + x) > rightX) or (Land[cpY + y, cpX + x] <> 0)) then + ((cpX + x) < leftX) or ((cpX + x) > rightX) or (LandGet(cpY + y, cpX + x) <> 0)) then pt^[x]:= cWhiteColor else (pt^[x]):= cWhiteColor and (not AMask); @@ -1028,8 +1028,8 @@ yy:= Y div 2; end; - pixelsweep:= (Land[Y, X] <= lfAllObjMask) and ((LandPixels[yy, xx] and AMask) <> 0); - if (((Land[Y, X] and lfDamaged) <> 0) and ((Land[Y, X] and lfIndestructible) = 0)) or pixelsweep then + pixelsweep:= (LandGet(Y, X) <= lfAllObjMask) and ((LandPixelGet(yy, xx) and AMask) <> 0); + if (((LandGet(Y, X) and lfDamaged) <> 0) and ((LandGet(Y, X) and lfIndestructible) = 0)) or pixelsweep then begin c:= 0; for i:= -1 to 1 do @@ -1047,27 +1047,27 @@ ny:= Y div 2 + i; nx:= X div 2 + j; if ((ny and (LAND_HEIGHT_MASK div 2)) = 0) and ((nx and (LAND_WIDTH_MASK div 2)) = 0) then - if (LandPixels[ny, nx] and AMASK) <> 0 then + if (LandPixelGet(ny, nx) and AMASK) <> 0 then inc(c); end - else if (LandPixels[ny, nx] and AMASK) <> 0 then + else if (LandPixelGet(ny, nx) and AMASK) <> 0 then inc(c); end - else if Land[ny, nx] > 255 then + else if LandGet(ny, nx) > 255 then inc(c); end end; if c < 4 then // 0-3 neighbours begin - if ((Land[Y, X] and lfBasic) <> 0) and (not disableLandBack) then - LandPixels[yy, xx]:= LandBackPixel(X, Y) + if ((LandGet(Y, X) and lfBasic) <> 0) and (not disableLandBack) then + LandPixelSet(yy, xx, LandBackPixel(X, Y)) else - LandPixels[yy, xx]:= LandPixels[yy, xx] and (not AMASK); + LandPixelSet(yy, xx, LandPixelGet(yy, xx) and (not AMASK)); if not pixelsweep then begin - Land[Y, X]:= 0; + LandSet(Y, X, 0); exit end end; @@ -1083,7 +1083,7 @@ begin // only AA inwards -if (Land[Y, X] and lfDamaged) = 0 then +if (LandGet(Y, X) and lfDamaged) = 0 then exit; // check location @@ -1104,9 +1104,9 @@ for nx:= X-1 to X+1 do for ny:= Y-1 to Y+1 do // only consider undamaged neighbors (also leads to skipping itself) - if (Land[ny, nx] and lfDamaged) = 0 then + if (LandGet(ny, nx) and lfDamaged) = 0 then begin - pixel:= LandPixels[ny, nx]; + pixel:= LandPixelGet(ny, nx); inc(r, (pixel and RMask) shr RShift); inc(g, (pixel and GMask) shr GShift); inc(b, (pixel and BMask) shr BShift); @@ -1132,95 +1132,95 @@ g:= g div 8; b:= b div 8; a:= a div 8; -LandPixels[y,x]:= (r shl RShift) or (g shl GShift) or (b shl BShift) or (a shl AShift); +LandPixelSet(y, x, (r shl RShift) or (g shl GShift) or (b shl BShift) or (a shl AShift)); end; procedure Smooth_oldImpl(X, Y: LongInt); begin // a bit of AA for explosions -if (Land[Y, X] = 0) and (Y > topY + 1) and +if (LandGet(Y, X) = 0) and (Y > topY + 1) and (Y < LAND_HEIGHT-2) and (X > leftX + 1) and (X < rightX - 1) then begin - if ((((Land[y, x-1] and lfDamaged) <> 0) and (((Land[y+1,x] and lfDamaged) <> 0)) or ((Land[y-1,x] and lfDamaged) <> 0)) - or (((Land[y, x+1] and lfDamaged) <> 0) and (((Land[y-1,x] and lfDamaged) <> 0) or ((Land[y+1,x] and lfDamaged) <> 0)))) then + if ((((LandGet(y, x-1) and lfDamaged) <> 0) and (((LandGet(y+1,x) and lfDamaged) <> 0)) or ((LandGet(y-1,x) and lfDamaged) <> 0)) + or (((LandGet(y, x+1) and lfDamaged) <> 0) and (((LandGet(y-1,x) and lfDamaged) <> 0) or ((LandGet(y+1,x) and lfDamaged) <> 0)))) then begin if (cReducedQuality and rqBlurryLand) = 0 then begin - if ((LandPixels[y,x] and AMask) shr AShift) < 10 then - LandPixels[y,x]:= (ExplosionBorderColor and (not AMask)) or (128 shl AShift) + if ((LandPixelGet(y,x) and AMask) shr AShift) < 10 then + LandPixelSet(y,x, (ExplosionBorderColor and (not AMask)) or (128 shl AShift)) else - LandPixels[y,x]:= - (((((LandPixels[y,x] and RMask shr RShift) div 2)+((ExplosionBorderColor and RMask) shr RShift) div 2) and $FF) shl RShift) or - (((((LandPixels[y,x] and GMask shr GShift) div 2)+((ExplosionBorderColor and GMask) shr GShift) div 2) and $FF) shl GShift) or - (((((LandPixels[y,x] and BMask shr BShift) div 2)+((ExplosionBorderColor and BMask) shr BShift) div 2) and $FF) shl BShift) or ($FF shl AShift) + LandPixelSet(y,x, + (((((LandPixelGet(y,x) and RMask shr RShift) div 2)+((ExplosionBorderColor and RMask) shr RShift) div 2) and $FF) shl RShift) or + (((((LandPixelGet(y,x) and GMask shr GShift) div 2)+((ExplosionBorderColor and GMask) shr GShift) div 2) and $FF) shl GShift) or + (((((LandPixelGet(y,x) and BMask shr BShift) div 2)+((ExplosionBorderColor and BMask) shr BShift) div 2) and $FF) shl BShift) or ($FF shl AShift)) end; { - if (Land[y, x-1] = lfObject) then - Land[y,x]:= lfObject - else if (Land[y, x+1] = lfObject) then - Land[y,x]:= lfObject + if (LandGet(y, x-1) = lfObject) then + LandSet(y,x, lfObject) + else if (LandGet(y, x+1) = lfObject) then + LandSet(y,x, lfObject) else - Land[y,x]:= lfBasic; + LandSet(y,x, lfBasic); } end - else if ((((Land[y, x-1] and lfDamaged) <> 0) and ((Land[y+1,x-1] and lfDamaged) <> 0) and ((Land[y+2,x] and lfDamaged) <> 0)) - or (((Land[y, x-1] and lfDamaged) <> 0) and ((Land[y-1,x-1] and lfDamaged) <> 0) and ((Land[y-2,x] and lfDamaged) <> 0)) - or (((Land[y, x+1] and lfDamaged) <> 0) and ((Land[y+1,x+1] and lfDamaged) <> 0) and ((Land[y+2,x] and lfDamaged) <> 0)) - or (((Land[y, x+1] and lfDamaged) <> 0) and ((Land[y-1,x+1] and lfDamaged) <> 0) and ((Land[y-2,x] and lfDamaged) <> 0)) - or (((Land[y+1, x] and lfDamaged) <> 0) and ((Land[y+1,x+1] and lfDamaged) <> 0) and ((Land[y,x+2] and lfDamaged) <> 0)) - or (((Land[y-1, x] and lfDamaged) <> 0) and ((Land[y-1,x+1] and lfDamaged) <> 0) and ((Land[y,x+2] and lfDamaged) <> 0)) - or (((Land[y+1, x] and lfDamaged) <> 0) and ((Land[y+1,x-1] and lfDamaged) <> 0) and ((Land[y,x-2] and lfDamaged) <> 0)) - or (((Land[y-1, x] and lfDamaged) <> 0) and ((Land[y-1,x-1] and lfDamaged) <> 0) and ((Land[y,x-2] and lfDamaged) <> 0))) then + else if ((((LandGet(y, x-1) and lfDamaged) <> 0) and ((LandGet(y+1,x-1) and lfDamaged) <> 0) and ((LandGet(y+2,x) and lfDamaged) <> 0)) + or (((LandGet(y, x-1) and lfDamaged) <> 0) and ((LandGet(y-1,x-1) and lfDamaged) <> 0) and ((LandGet(y-2,x) and lfDamaged) <> 0)) + or (((LandGet(y, x+1) and lfDamaged) <> 0) and ((LandGet(y+1,x+1) and lfDamaged) <> 0) and ((LandGet(y+2,x) and lfDamaged) <> 0)) + or (((LandGet(y, x+1) and lfDamaged) <> 0) and ((LandGet(y-1,x+1) and lfDamaged) <> 0) and ((LandGet(y-2,x) and lfDamaged) <> 0)) + or (((LandGet(y+1, x) and lfDamaged) <> 0) and ((LandGet(y+1,x+1) and lfDamaged) <> 0) and ((LandGet(y,x+2) and lfDamaged) <> 0)) + or (((LandGet(y-1, x) and lfDamaged) <> 0) and ((LandGet(y-1,x+1) and lfDamaged) <> 0) and ((LandGet(y,x+2) and lfDamaged) <> 0)) + or (((LandGet(y+1, x) and lfDamaged) <> 0) and ((LandGet(y+1,x-1) and lfDamaged) <> 0) and ((LandGet(y,x-2) and lfDamaged) <> 0)) + or (((LandGet(y-1, x) and lfDamaged) <> 0) and ((LandGet(y-1,x-1) and lfDamaged) <> 0) and ((LandGet(y,x-2) and lfDamaged) <> 0))) then begin if (cReducedQuality and rqBlurryLand) = 0 then begin - if ((LandPixels[y,x] and AMask) shr AShift) < 10 then - LandPixels[y,x]:= (ExplosionBorderColor and (not AMask)) or (64 shl AShift) + if ((LandPixelGet(y,x) and AMask) shr AShift) < 10 then + LandPixelSet(y,x, (ExplosionBorderColor and (not AMask)) or (64 shl AShift)) else - LandPixels[y,x]:= - (((((LandPixels[y,x] and RMask shr RShift) * 3 div 4)+((ExplosionBorderColor and RMask) shr RShift) div 4) and $FF) shl RShift) or - (((((LandPixels[y,x] and GMask shr GShift) * 3 div 4)+((ExplosionBorderColor and GMask) shr GShift) div 4) and $FF) shl GShift) or - (((((LandPixels[y,x] and BMask shr BShift) * 3 div 4)+((ExplosionBorderColor and BMask) shr BShift) div 4) and $FF) shl BShift) or ($FF shl AShift) + LandPixelSet(y,x, + (((((LandPixelGet(y,x) and RMask shr RShift) * 3 div 4)+((ExplosionBorderColor and RMask) shr RShift) div 4) and $FF) shl RShift) or + (((((LandPixelGet(y,x) and GMask shr GShift) * 3 div 4)+((ExplosionBorderColor and GMask) shr GShift) div 4) and $FF) shl GShift) or + (((((LandPixelGet(y,x) and BMask shr BShift) * 3 div 4)+((ExplosionBorderColor and BMask) shr BShift) div 4) and $FF) shl BShift) or ($FF shl AShift)) end; { - if (Land[y, x-1] = lfObject) then - Land[y, x]:= lfObject - else if (Land[y, x+1] = lfObject) then - Land[y, x]:= lfObject - else if (Land[y+1, x] = lfObject) then - Land[y, x]:= lfObject - else if (Land[y-1, x] = lfObject) then - Land[y, x]:= lfObject - else Land[y,x]:= lfBasic + if (LandGet(y, x-1) = lfObject) then + LandGet(y, x):= lfObject + else if (LandGet(y, x+1) = lfObject) then + LandGet(y, x):= lfObject + else if (LandGet(y+1, x) = lfObject) then + LandGet(y, x):= lfObject + else if (LandGet(y-1, x) = lfObject) then + LandGet(y, x):= lfObject + else LandGet(y,x):= lfBasic } end end -else if ((cReducedQuality and rqBlurryLand) = 0) and ((LandPixels[Y, X] and AMask) = AMask) -and (Land[Y, X] and (lfDamaged or lfBasic) = lfBasic) +else if ((cReducedQuality and rqBlurryLand) = 0) and ((LandPixelGet(Y, X) and AMask) = AMask) +and (LandGet(Y, X) and (lfDamaged or lfBasic) = lfBasic) and (Y > topY + 1) and (Y < LAND_HEIGHT-2) and (X > leftX + 1) and (X < rightX - 1) then begin - if ((((Land[y, x-1] and lfDamaged) <> 0) and (((Land[y+1,x] and lfDamaged) <> 0)) or ((Land[y-1,x] and lfDamaged) <> 0)) - or (((Land[y, x+1] and lfDamaged) <> 0) and (((Land[y-1,x] and lfDamaged) <> 0) or ((Land[y+1,x] and lfDamaged) <> 0)))) then + if ((((LandGet(y, x-1) and lfDamaged) <> 0) and (((LandGet(y+1,x) and lfDamaged) <> 0)) or ((LandGet(y-1,x) and lfDamaged) <> 0)) + or (((LandGet(y, x+1) and lfDamaged) <> 0) and (((LandGet(y-1,x) and lfDamaged) <> 0) or ((LandGet(y+1,x) and lfDamaged) <> 0)))) then begin - LandPixels[y,x]:= - (((((LandPixels[y,x] and RMask shr RShift) div 2)+((ExplosionBorderColor and RMask) shr RShift) div 2) and $FF) shl RShift) or - (((((LandPixels[y,x] and GMask shr GShift) div 2)+((ExplosionBorderColor and GMask) shr GShift) div 2) and $FF) shl GShift) or - (((((LandPixels[y,x] and BMask shr BShift) div 2)+((ExplosionBorderColor and BMask) shr BShift) div 2) and $FF) shl BShift) or ($FF shl AShift) + LandPixelSet(y,x, + (((((LandPixelGet(y,x) and RMask shr RShift) div 2)+((ExplosionBorderColor and RMask) shr RShift) div 2) and $FF) shl RShift) or + (((((LandPixelGet(y,x) and GMask shr GShift) div 2)+((ExplosionBorderColor and GMask) shr GShift) div 2) and $FF) shl GShift) or + (((((LandPixelGet(y,x) and BMask shr BShift) div 2)+((ExplosionBorderColor and BMask) shr BShift) div 2) and $FF) shl BShift) or ($FF shl AShift)) end - else if ((((Land[y, x-1] and lfDamaged) <> 0) and ((Land[y+1,x-1] and lfDamaged) <> 0) and ((Land[y+2,x] and lfDamaged) <> 0)) - or (((Land[y, x-1] and lfDamaged) <> 0) and ((Land[y-1,x-1] and lfDamaged) <> 0) and ((Land[y-2,x] and lfDamaged) <> 0)) - or (((Land[y, x+1] and lfDamaged) <> 0) and ((Land[y+1,x+1] and lfDamaged) <> 0) and ((Land[y+2,x] and lfDamaged) <> 0)) - or (((Land[y, x+1] and lfDamaged) <> 0) and ((Land[y-1,x+1] and lfDamaged) <> 0) and ((Land[y-2,x] and lfDamaged) <> 0)) - or (((Land[y+1, x] and lfDamaged) <> 0) and ((Land[y+1,x+1] and lfDamaged) <> 0) and ((Land[y,x+2] and lfDamaged) <> 0)) - or (((Land[y-1, x] and lfDamaged) <> 0) and ((Land[y-1,x+1] and lfDamaged) <> 0) and ((Land[y,x+2] and lfDamaged) <> 0)) - or (((Land[y+1, x] and lfDamaged) <> 0) and ((Land[y+1,x-1] and lfDamaged) <> 0) and ((Land[y,x-2] and lfDamaged) <> 0)) - or (((Land[y-1, x] and lfDamaged) <> 0) and ((Land[y-1,x-1] and lfDamaged) <> 0) and ((Land[y,x-2] and lfDamaged) <> 0))) then + else if ((((LandGet(y, x-1) and lfDamaged) <> 0) and ((LandGet(y+1,x-1) and lfDamaged) <> 0) and ((LandGet(y+2,x) and lfDamaged) <> 0)) + or (((LandGet(y, x-1) and lfDamaged) <> 0) and ((LandGet(y-1,x-1) and lfDamaged) <> 0) and ((LandGet(y-2,x) and lfDamaged) <> 0)) + or (((LandGet(y, x+1) and lfDamaged) <> 0) and ((LandGet(y+1,x+1) and lfDamaged) <> 0) and ((LandGet(y+2,x) and lfDamaged) <> 0)) + or (((LandGet(y, x+1) and lfDamaged) <> 0) and ((LandGet(y-1,x+1) and lfDamaged) <> 0) and ((LandGet(y-2,x) and lfDamaged) <> 0)) + or (((LandGet(y+1, x) and lfDamaged) <> 0) and ((LandGet(y+1,x+1) and lfDamaged) <> 0) and ((LandGet(y,x+2) and lfDamaged) <> 0)) + or (((LandGet(y-1, x) and lfDamaged) <> 0) and ((LandGet(y-1,x+1) and lfDamaged) <> 0) and ((LandGet(y,x+2) and lfDamaged) <> 0)) + or (((LandGet(y+1, x) and lfDamaged) <> 0) and ((LandGet(y+1,x-1) and lfDamaged) <> 0) and ((LandGet(y,x-2) and lfDamaged) <> 0)) + or (((LandGet(y-1, x) and lfDamaged) <> 0) and ((LandGet(y-1,x-1) and lfDamaged) <> 0) and ((LandGet(y,x-2) and lfDamaged) <> 0))) then begin - LandPixels[y,x]:= - (((((LandPixels[y,x] and RMask shr RShift) * 3 div 4)+((ExplosionBorderColor and RMask) shr RShift) div 4) and $FF) shl RShift) or - (((((LandPixels[y,x] and GMask shr GShift) * 3 div 4)+((ExplosionBorderColor and GMask) shr GShift) div 4) and $FF) shl GShift) or - (((((LandPixels[y,x] and BMask shr BShift) * 3 div 4)+((ExplosionBorderColor and BMask) shr BShift) div 4) and $FF) shl BShift) or ($FF shl AShift) + LandPixelSet(y,x, + (((((LandPixelGet(y,x) and RMask shr RShift) * 3 div 4)+((ExplosionBorderColor and RMask) shr RShift) div 4) and $FF) shl RShift) or + (((((LandPixelGet(y,x) and GMask shr GShift) * 3 div 4)+((ExplosionBorderColor and GMask) shr GShift) div 4) and $FF) shl GShift) or + (((((LandPixelGet(y,x) and BMask shr BShift) * 3 div 4)+((ExplosionBorderColor and BMask) shr BShift) div 4) and $FF) shl BShift) or ($FF shl AShift)) end end end; @@ -1308,12 +1308,12 @@ // Return true if outside of land or not the value tested, used right now for some X/Y movement that does not use normal hedgehog movement in GSHandlers.inc -function CheckLandValue(X, Y: LongInt; LandFlag: Word): boolean; inline; +function CheckLandValue(X, Y: LongInt; LandFlag: Word): boolean; begin - CheckLandValue:= ((X and LAND_WIDTH_MASK <> 0) or (Y and LAND_HEIGHT_MASK <> 0)) or ((Land[Y, X] and LandFlag) = 0) + CheckLandValue:= ((X and LAND_WIDTH_MASK <> 0) or (Y and LAND_HEIGHT_MASK <> 0)) or ((LandGet(Y, X) and LandFlag) = 0) end; -function LandBackPixel(x, y: LongInt): LongWord; inline; +function LandBackPixel(x, y: LongInt): LongWord; var p: PLongWordArray; begin if LandBackSurface = nil then @@ -1382,30 +1382,30 @@ end; if ((x and LAND_WIDTH_MASK) = 0) and ((y and LAND_HEIGHT_MASK) = 0) then - Land[y, x]:= Color; + LandSet(y, x, Color); end end; -function DrawDots(x, y, xx, yy: Longint; Color: Longword): Longword; inline; +function DrawDots(x, y, xx, yy: Longint; Color: Longword): Longword; begin DrawDots:= 0; - if (((x + xx) and LAND_WIDTH_MASK) = 0) and (((y + yy) and LAND_HEIGHT_MASK) = 0) and (Land[y + yy, x + xx] <> Color) then - begin inc(DrawDots); Land[y + yy, x + xx]:= Color; end; - if (((x + xx) and LAND_WIDTH_MASK) = 0) and (((y - yy) and LAND_HEIGHT_MASK) = 0) and (Land[y - yy, x + xx] <> Color) then - begin inc(DrawDots); Land[y - yy, x + xx]:= Color; end; - if (((x - xx) and LAND_WIDTH_MASK) = 0) and (((y + yy) and LAND_HEIGHT_MASK) = 0) and (Land[y + yy, x - xx] <> Color) then - begin inc(DrawDots); Land[y + yy, x - xx]:= Color; end; - if (((x - xx) and LAND_WIDTH_MASK) = 0) and (((y - yy) and LAND_HEIGHT_MASK) = 0) and (Land[y - yy, x - xx] <> Color) then - begin inc(DrawDots); Land[y - yy, x - xx]:= Color; end; - if (((x + yy) and LAND_WIDTH_MASK) = 0) and (((y + xx) and LAND_HEIGHT_MASK) = 0) and (Land[y + xx, x + yy] <> Color) then - begin inc(DrawDots); Land[y + xx, x + yy]:= Color; end; - if (((x + yy) and LAND_WIDTH_MASK) = 0) and (((y - xx) and LAND_HEIGHT_MASK) = 0) and (Land[y - xx, x + yy] <> Color) then - begin inc(DrawDots); Land[y - xx, x + yy]:= Color; end; - if (((x - yy) and LAND_WIDTH_MASK) = 0) and (((y + xx) and LAND_HEIGHT_MASK) = 0) and (Land[y + xx, x - yy] <> Color) then - begin inc(DrawDots); Land[y + xx, x - yy]:= Color; end; - if (((x - yy) and LAND_WIDTH_MASK) = 0) and (((y - xx) and LAND_HEIGHT_MASK) = 0) and (Land[y - xx, x - yy] <> Color) then - begin inc(DrawDots); Land[y - xx, x - yy]:= Color; end; + if (((x + xx) and LAND_WIDTH_MASK) = 0) and (((y + yy) and LAND_HEIGHT_MASK) = 0) and (LandGet(y + yy, x + xx) <> Color) then + begin inc(DrawDots); LandSet(y + yy, x + xx, Color); end; + if (((x + xx) and LAND_WIDTH_MASK) = 0) and (((y - yy) and LAND_HEIGHT_MASK) = 0) and (LandGet(y - yy, x + xx) <> Color) then + begin inc(DrawDots); LandSet(y - yy, x + xx, Color); end; + if (((x - xx) and LAND_WIDTH_MASK) = 0) and (((y + yy) and LAND_HEIGHT_MASK) = 0) and (LandGet(y + yy, x - xx) <> Color) then + begin inc(DrawDots); LandSet(y + yy, x - xx, Color); end; + if (((x - xx) and LAND_WIDTH_MASK) = 0) and (((y - yy) and LAND_HEIGHT_MASK) = 0) and (LandGet(y - yy, x - xx) <> Color) then + begin inc(DrawDots); LandSet(y - yy, x - xx, Color); end; + if (((x + yy) and LAND_WIDTH_MASK) = 0) and (((y + xx) and LAND_HEIGHT_MASK) = 0) and (LandGet(y + xx, x + yy) <> Color) then + begin inc(DrawDots); LandSet(y + xx, x + yy, Color); end; + if (((x + yy) and LAND_WIDTH_MASK) = 0) and (((y - xx) and LAND_HEIGHT_MASK) = 0) and (LandGet(y - xx, x + yy) <> Color) then + begin inc(DrawDots); LandSet(y - xx, x + yy, Color); end; + if (((x - yy) and LAND_WIDTH_MASK) = 0) and (((y + xx) and LAND_HEIGHT_MASK) = 0) and (LandGet(y + xx, x - yy) <> Color) then + begin inc(DrawDots); LandSet(y + xx, x - yy, Color); end; + if (((x - yy) and LAND_WIDTH_MASK) = 0) and (((y - xx) and LAND_HEIGHT_MASK) = 0) and (LandGet(y - xx, x - yy) <> Color) then + begin inc(DrawDots); LandSet(y - xx, x - yy, Color); end; end; function DrawLines(X1, Y1, X2, Y2, XX, YY: LongInt; color: Longword): Longword; @@ -1512,9 +1512,9 @@ xx:= dx - r + x; if (xx = x) and (yy = y) then s[dx + 1]:= 'X' - else if Land[yy, xx] > 255 then + else if LandGet(yy, xx) > 255 then s[dx + 1]:= 'O' - else if Land[yy, xx] > 0 then + else if LandGet(yy, xx) > 0 then s[dx + 1]:= '*' else s[dx + 1]:= '.' diff -r 2b4f361e3891 -r 629d5123a979 hedgewars/uLandObjects.pas --- a/hedgewars/uLandObjects.pas Sat Sep 28 22:27:13 2024 +0200 +++ b/hedgewars/uLandObjects.pas Tue Dec 31 15:19:43 2024 +0100 @@ -25,18 +25,18 @@ procedure AddObjects(); procedure FreeLandObjects(); procedure LoadThemeConfig; -procedure BlitImageAndGenerateCollisionInfo(cpX, cpY, Width: Longword; Image: PSDL_Surface); inline; -procedure BlitImageAndGenerateCollisionInfo(cpX, cpY, Width: Longword; Image: PSDL_Surface; LandFlags: Word); inline; +procedure BlitImageAndGenerateCollisionInfo(cpX, cpY, Width: Longword; Image: PSDL_Surface); +procedure BlitImageAndGenerateCollisionInfo(cpX, cpY, Width: Longword; Image: PSDL_Surface; LandFlags: Word); procedure BlitImageAndGenerateCollisionInfo(cpX, cpY, Width: Longword; Image: PSDL_Surface; LandFlags: Word; Flip: boolean); procedure BlitOverlayAndGenerateCollisionInfo(cpX, cpY: Longword; Image: PSDL_Surface); procedure BlitImageUsingMask(cpX, cpY: Longword; Image, Mask: PSDL_Surface); procedure AddOnLandObjects(Surface: PSDL_Surface); -procedure SetLand(var LandWord: Word; Pixel: LongWord); inline; +procedure SetLand(y, x: LongInt; Pixel: LongWord); implementation uses uStore, uConsts, uConsole, uRandom, uSound , uTypes, uVariables, uDebug, uUtils - , uPhysFSLayer, uRenderUtils; + , uPhysFSLayer, uRenderUtils, uLandUtils; const MaxRects = 512; MAXOBJECTRECTS = 16; @@ -84,37 +84,37 @@ ThemeObjects: TThemeObjects; SprayObjects: TSprayObjects; -procedure SetLand(var LandWord: Word; Pixel: LongWord); inline; +procedure SetLand(y, x: LongInt; Pixel: LongWord); begin // this an if instead of masking colours to avoid confusing map creators if ((AMask and Pixel) = 0) then - LandWord:= 0 + LandSet(y, x, 0) else if (Pixel and AMask > 0) and (Pixel and RMask > 0) and (Pixel and GMask > 0) and (Pixel and BMask > 0) then // whiteish - LandWord:= lfObject + LandSet(y, x, lfObject) else if (Pixel and AMask > 0) and (Pixel and RMask = 0) and (Pixel and GMask = 0) and (Pixel and BMask = 0) then // blackish begin - LandWord:= lfBasic; + LandSet(y, x, lfBasic); disableLandBack:= false end else if (Pixel and AMask > 0) and (Pixel and RMask > 0) and (Pixel and GMask = 0) and (Pixel and BMask = 0) then // reddish - LandWord:= lfIndestructible + LandSet(y, x, lfIndestructible) else if (Pixel and AMask > 0) and (Pixel and RMask = 0) and (Pixel and GMask = 0) and (Pixel and BMask > 0) then // blueish - LandWord:= lfObject or lfIce + LandSet(y, x, lfObject or lfIce) else if (Pixel and AMask > 0) and (Pixel and RMask = 0) and (Pixel and GMask > 0) and (Pixel and BMask = 0) then // greenish - LandWord:= lfObject or lfBouncy + LandSet(y, x, lfObject or lfBouncy) end; -procedure BlitImageAndGenerateCollisionInfo(cpX, cpY, Width: Longword; Image: PSDL_Surface); inline; +procedure BlitImageAndGenerateCollisionInfo(cpX, cpY, Width: Longword; Image: PSDL_Surface); begin BlitImageAndGenerateCollisionInfo(cpX, cpY, Width, Image, 0, false); end; -procedure BlitImageAndGenerateCollisionInfo(cpX, cpY, Width: Longword; Image: PSDL_Surface; LandFlags: Word); inline; +procedure BlitImageAndGenerateCollisionInfo(cpX, cpY, Width: Longword; Image: PSDL_Surface; LandFlags: Word); begin BlitImageAndGenerateCollisionInfo(cpX, cpY, Width, Image, LandFlags, false); end; -function LerpByte(src, dst: Byte; l: LongWord): LongWord; inline; +function LerpByte(src, dst: Byte; l: LongWord): LongWord; begin LerpByte:= ((255 - l) * src + l * dst) div 255; end; @@ -153,9 +153,9 @@ color:= p^[x]; if (cReducedQuality and rqBlurryLand) = 0 then - pLandColor:= @LandPixels[cpY + y, cpX + x] + pLandColor:= @(LandPixelRow(cpY + y)^[cpX + x]) else - pLandColor:= @LandPixels[(cpY + y) div 2, (cpX + x) div 2]; + pLandColor:= @(LandPixelRow((cpY + y) div 2)^[(cpX + x) div 2]); landColor:= pLandColor^; alpha:= (landColor and AMask) shr AShift; @@ -173,8 +173,8 @@ end; - if ((color and AMask) <> 0) and (Land[cpY + y, cpX + x] <= lfAllObjMask) then - Land[cpY + y, cpX + x]:= lfObject or LandFlags + if ((color and AMask) <> 0) and (LandGet(cpY + y, cpX + x) <= lfAllObjMask) then + LandSet(cpY + y, cpX + x, lfObject or LandFlags) end; p:= PLongwordArray(@(p^[Image^.pitch shr 2])) end; @@ -208,9 +208,9 @@ if (color and AMask) <> 0 then begin if (cReducedQuality and rqBlurryLand) = 0 then - pLandColor:= @LandPixels[cpY + y, cpX + x] + pLandColor:= @(LandPixelRow(cpY + y)^[cpX + x]) else - pLandColor:= @LandPixels[(cpY + y) div 2, (cpX + x) div 2]; + pLandColor:= @(LandPixelRow((cpY + y) div 2)^[(cpX + x) div 2]); alpha:= (color and AMask) shr AShift; if ((alpha <> $FF) and ((pLandColor^) <> 0)) then @@ -224,8 +224,8 @@ end; pLandColor^:= color; - if Land[cpY + y, cpX + x] <= lfAllObjMask then - Land[cpY + y, cpX + x]:= lfObject + if LandGet(cpY + y, cpX + x) <= lfAllObjMask then + LandSet(cpY + y, cpX + x, lfObject) end; end; p:= PLongwordArray(@(p^[Image^.pitch shr 2])) @@ -263,9 +263,9 @@ color:= p^[x]; if (cReducedQuality and rqBlurryLand) = 0 then - pLandColor:= @LandPixels[cpY + y, cpX + x] + pLandColor:= @(LandPixelRow(cpY + y)^[cpX + x]) else - pLandColor:= @LandPixels[(cpY + y) div 2, (cpX + x) div 2]; + pLandColor:= @(LandPixelRow((cpY + y) div 2)^[(cpX + x) div 2]); landColor:= pLandColor^; alpha:= (landColor and AMask) shr AShift; @@ -282,8 +282,8 @@ or (LerpByte(alpha, 255, (color and AMask) shr AShift) shl AShift); end; - if (Land[cpY + y, cpX + x] <= lfAllObjMask) or (Land[cpY + y, cpX + x] and lfObject <> 0) then - SetLand(Land[cpY + y, cpX + x], mp^[x]); + if (LandGet(cpY + y, cpX + x) <= lfAllObjMask) or (LandGet(cpY + y, cpX + x) and lfObject <> 0) then + SetLand(cpY + y, cpX + x, mp^[x]); end; p:= PLongwordArray(@(p^[Image^.pitch shr 2])); @@ -341,7 +341,7 @@ begin lRes:= 0; for i:= y to Pred(y + h) do - if Land[i, x] <> 0 then + if LandGet(i, x) <> 0 then inc(lRes); CountNonZeroz:= lRes; end; @@ -425,8 +425,8 @@ begin bRes:= ((rect.y and LAND_HEIGHT_MASK) = 0) and ((by and LAND_HEIGHT_MASK) = 0) and ((tmpx and LAND_WIDTH_MASK) = 0) and ((tmpx2 and LAND_WIDTH_MASK) = 0) - and (Land[rect.y, tmpx] = Color) and (Land[by, tmpx] = Color) - and (Land[rect.y, tmpx2] = Color) and (Land[by, tmpx2] = Color); + and (LandGet(rect.y, tmpx) = Color) and (LandGet(by, tmpx) = Color) + and (LandGet(rect.y, tmpx2) = Color) and (LandGet(by, tmpx2) = Color); inc(tmpx); dec(tmpx2) end; @@ -436,8 +436,8 @@ begin bRes:= ((tmpy and LAND_HEIGHT_MASK) = 0) and ((tmpy2 and LAND_HEIGHT_MASK) = 0) and ((rect.x and LAND_WIDTH_MASK) = 0) and ((bx and LAND_WIDTH_MASK) = 0) - and (Land[tmpy, rect.x] = Color) and (Land[tmpy, bx] = Color) - and (Land[tmpy2, rect.x] = Color) and (Land[tmpy2, bx] = Color); + and (LandGet(tmpy, rect.x) = Color) and (LandGet(tmpy, bx) = Color) + and (LandGet(tmpy2, rect.x) = Color) and (LandGet(tmpy2, bx) = Color); inc(tmpy); dec(tmpy2) end; @@ -459,7 +459,7 @@ begin for tmpx := rect.x to bx do begin - if (((Land[rect.y, tmpx] and LandType) or (Land[by, tmpx] and LandType)) <> 0) then + if (((LandGet(rect.y, tmpx) and LandType) or (LandGet(by, tmpx) and LandType)) <> 0) then begin CheckLandAny := true; exit; @@ -467,7 +467,7 @@ end; for tmpy := rect.y to by do begin - if (((Land[tmpy, rect.x] and LandType) or (Land[tmpy, bx] and LandType)) <> 0) then + if (((LandGet(tmpy, rect.x) and LandType) or (LandGet(tmpy, bx) and LandType)) <> 0) then begin CheckLandAny := true; exit; diff -r 2b4f361e3891 -r 629d5123a979 hedgewars/uLandOutline.pas --- a/hedgewars/uLandOutline.pas Sat Sep 28 22:27:13 2024 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,218 +0,0 @@ -unit uLandOutline; - -interface - -uses uConsts, SDLh, uFloat; - -type TPixAr = record - Count: Longword; - ar: array[0..Pred(cMaxEdgePoints)] of TPoint; - end; - -procedure DrawEdge(var pa: TPixAr; value: Word); -procedure FillLand(x, y: LongInt; border, value: Word); -procedure BezierizeEdge(var pa: TPixAr; Delta: hwFloat); - -implementation - -uses uLandGraphics, uDebug, uVariables, uLandTemplates; - - -var Stack: record - Count: Longword; - points: array[0..8192] of record - xl, xr, y, dir: LongInt; - end - end; - - -procedure Push(_xl, _xr, _y, _dir: LongInt); -begin - if checkFails(Stack.Count <= 8192, 'FillLand: stack overflow', true) then exit; - _y:= _y + _dir; - if (_y < 0) or (_y >= LAND_HEIGHT) then - exit; - with Stack.points[Stack.Count] do - begin - xl:= _xl; - xr:= _xr; - y:= _y; - dir:= _dir - end; - inc(Stack.Count) -end; - -procedure Pop(var _xl, _xr, _y, _dir: LongInt); -begin - dec(Stack.Count); - with Stack.points[Stack.Count] do - begin - _xl:= xl; - _xr:= xr; - _y:= y; - _dir:= dir - end -end; - -procedure FillLand(x, y: LongInt; border, value: Word); -var xl, xr, dir: LongInt; -begin - Stack.Count:= 0; - xl:= x - 1; - xr:= x; - Push(xl, xr, y, -1); - Push(xl, xr, y, 1); - dir:= 0; - while Stack.Count > 0 do - begin - Pop(xl, xr, y, dir); - while (xl > 0) and (Land[y, xl] <> border) and (Land[y, xl] <> value) do - dec(xl); - while (xr < LAND_WIDTH - 1) and (Land[y, xr] <> border) and (Land[y, xr] <> value) do - inc(xr); - while (xl < xr) do - begin - while (xl <= xr) and ((Land[y, xl] = border) or (Land[y, xl] = value)) do - inc(xl); - x:= xl; - while (xl <= xr) and (Land[y, xl] <> border) and (Land[y, xl] <> value) do - begin - Land[y, xl]:= value; - inc(xl) - end; - if x < xl then - begin - Push(x, Pred(xl), y, dir); - Push(x, Pred(xl), y,-dir); - end; - end; - end; -end; - -procedure DrawEdge(var pa: TPixAr; value: Word); -var i: LongInt; -begin - i:= 0; - with pa do - while i < LongInt(Count) - 1 do - if (ar[i + 1].X = NTPX) then - inc(i, 2) - else - begin - DrawLine(ar[i].x, ar[i].y, ar[i + 1].x, ar[i + 1].y, value); - inc(i) - end -end; - - -procedure Vector(p1, p2, p3: TPoint; var Vx, Vy: hwFloat); -var d1, d2, d: hwFloat; -begin - Vx:= int2hwFloat(p1.X - p3.X); - Vy:= int2hwFloat(p1.Y - p3.Y); - - d2:= Distance(Vx, Vy); - - if d2.QWordValue = 0 then - begin - Vx:= _0; - Vy:= _0 - end - else - begin - d:= DistanceI(p2.X - p1.X, p2.Y - p1.Y); - d1:= DistanceI(p2.X - p3.X, p2.Y - p3.Y); - - if d1 < d then - d:= d1; - if d2 < d then - d:= d2; - - d2:= d * _1div3 / d2; - - Vx:= Vx * d2; - Vy:= Vy * d2 - end -end; - -procedure AddLoopPoints(var pa, opa: TPixAr; StartI, EndI: LongInt; Delta: hwFloat); -var i, pi, ni: LongInt; - NVx, NVy, PVx, PVy: hwFloat; - x1, x2, y1, y2: LongInt; - tsq, tcb, t, r1, r2, r3, cx1, cx2, cy1, cy2: hwFloat; - X, Y: LongInt; -begin - if pa.Count < cMaxEdgePoints - 2 then - begin - pi:= EndI; - i:= StartI; - ni:= Succ(StartI); - {$HINTS OFF} - Vector(opa.ar[pi], opa.ar[i], opa.ar[ni], NVx, NVy); - {$HINTS ON} - repeat - i:= ni; - inc(pi); - if pi > EndI then - pi:= StartI; - inc(ni); - if ni > EndI then - ni:= StartI; - PVx:= NVx; - PVy:= NVy; - Vector(opa.ar[pi], opa.ar[i], opa.ar[ni], NVx, NVy); - - x1:= opa.ar[pi].x; - y1:= opa.ar[pi].y; - x2:= opa.ar[i].x; - y2:= opa.ar[i].y; - - cx1:= int2hwFloat(x1) - PVx; - cy1:= int2hwFloat(y1) - PVy; - cx2:= int2hwFloat(x2) + NVx; - cy2:= int2hwFloat(y2) + NVy; - t:= _0; - while (t.Round = 0) and (pa.Count < cMaxEdgePoints-2) do - begin - tsq:= t * t; - tcb:= tsq * t; - r1:= (_1 - t*3 + tsq*3 - tcb); - r2:= ( t*3 - tsq*6 + tcb*3); - r3:= ( tsq*3 - tcb*3); - X:= hwRound(r1 * x1 + r2 * cx1 + r3 * cx2 + tcb * x2); - Y:= hwRound(r1 * y1 + r2 * cy1 + r3 * cy2 + tcb * y2); - t:= t + Delta; - pa.ar[pa.Count].x:= X; - pa.ar[pa.Count].y:= Y; - inc(pa.Count); - //TryDo(pa.Count <= cMaxEdgePoints, 'Edge points overflow', true) - end; - until i = StartI; - end; - - pa.ar[pa.Count].x:= opa.ar[StartI].X; - pa.ar[pa.Count].y:= opa.ar[StartI].Y; - inc(pa.Count) -end; - -procedure BezierizeEdge(var pa: TPixAr; Delta: hwFloat); -var i, StartLoop: LongInt; - opa: TPixAr; -begin -opa:= pa; -pa.Count:= 0; -i:= 0; -StartLoop:= 0; -while (i < LongInt(opa.Count)) and (pa.Count < cMaxEdgePoints-1) do - if (opa.ar[i + 1].X = NTPX) then - begin - AddLoopPoints(pa, opa, StartLoop, i, Delta); - inc(i, 2); - StartLoop:= i; - pa.ar[pa.Count].X:= NTPX; - pa.ar[pa.Count].Y:= 0; - inc(pa.Count); - end else inc(i) -end; - -end. diff -r 2b4f361e3891 -r 629d5123a979 hedgewars/uLandTexture.pas --- a/hedgewars/uLandTexture.pas Sat Sep 28 22:27:13 2024 +0200 +++ b/hedgewars/uLandTexture.pas Tue Dec 31 15:19:43 2024 +0100 @@ -30,7 +30,7 @@ procedure SetLandTexture; implementation -uses uConsts, GLunit, uTypes, uVariables, uTextures, uDebug, uRender, uUtils; +uses uConsts, GLunit, uTypes, uVariables, uTextures, uDebug, uRender, uUtils, uLandUtils; const TEXSIZE = 128; // in avoid tile borders stretch the blurry texture by 1 pixel more @@ -50,7 +50,7 @@ var ty: Longword; begin for ty:= 0 to TEXSIZE - 1 do - Move(LandPixels[y * TEXSIZE + ty, x * TEXSIZE], tmpPixels[ty, 0], sizeof(Longword) * TEXSIZE); + Move(LandPixelRow(y * TEXSIZE + ty)^[x * TEXSIZE], tmpPixels[ty, 0], sizeof(Longword) * TEXSIZE); Pixels:= @tmpPixels end; @@ -60,7 +60,7 @@ begin for ty:= 0 to TEXSIZE - 1 do for tx:= 0 to TEXSIZE - 1 do - tmpPixels[ty, tx]:= Land[y * TEXSIZE + ty, x * TEXSIZE + tx] or AMask; + tmpPixels[ty, tx]:= LandGet(y * TEXSIZE + ty, x * TEXSIZE + tx) or AMask; Pixels2:= @tmpPixels end; @@ -129,14 +129,14 @@ // first check edges while isEmpty and (ty < TEXSIZE) do begin - isEmpty:= LandPixels[ly + ty, lx] and AMask = 0; - if isEmpty then isEmpty:= LandPixels[ly + ty, Pred(lx + TEXSIZE)] and AMask = 0; + isEmpty:= LandPixelGet(ly + ty, lx) and AMask = 0; + if isEmpty then isEmpty:= LandPixelGet(ly + ty, Pred(lx + TEXSIZE)) and AMask = 0; inc(ty) end; while isEmpty and (tx < TEXSIZE-1) do begin - isEmpty:= LandPixels[ly, lx + tx] and AMask = 0; - if isEmpty then isEmpty:= LandPixels[Pred(ly + TEXSIZE), lx + tx] and AMask = 0; + isEmpty:= LandPixelGet(ly, lx + tx) and AMask = 0; + if isEmpty then isEmpty:= LandPixelGet(Pred(ly + TEXSIZE), lx + tx) and AMask = 0; inc(tx) end; // then search every other remaining. does this sort of stuff defeat compiler opts? @@ -146,7 +146,7 @@ tx:= 2; while isEmpty and (tx < TEXSIZE-1) do begin - isEmpty:= LandPixels[ly + ty, lx + tx] and AMask = 0; + isEmpty:= LandPixelGet(ly + ty, lx + tx) and AMask = 0; inc(tx,2) end; inc(ty,2); @@ -158,7 +158,7 @@ tx:= 1; while isEmpty and (tx < TEXSIZE-1) do begin - isEmpty:= LandPixels[ly + ty, lx + tx] and AMask = 0; + isEmpty:= LandPixelGet(ly + ty, lx + tx) and AMask = 0; inc(tx,2) end; inc(ty,2); diff -r 2b4f361e3891 -r 629d5123a979 hedgewars/uLandUtils.pas --- a/hedgewars/uLandUtils.pas Sat Sep 28 22:27:13 2024 +0200 +++ b/hedgewars/uLandUtils.pas Tue Dec 31 15:19:43 2024 +0100 @@ -1,12 +1,159 @@ unit uLandUtils; interface +uses SDLh; +procedure GenerateOutlineTemplatedLand(featureSize: Longword; seed, templateType: shortstring; dataPath: ansistring); +procedure GenerateWfcTemplatedLand(featureSize: Longword; seed, templateType: shortstring; dataPath: ansistring); +procedure GenerateMazeLand(featureSize: Longword; seed, templateType: shortstring; dataPath: ansistring); procedure ResizeLand(width, height: LongWord); +procedure DisposeLand(); procedure InitWorldEdges(); +function LandGet(y, x: LongInt): Word; +procedure LandSet(y, x: LongInt; value: Word); +function LandRow(row: LongInt): PWordArray; + +procedure FillLand(x, y: LongInt; border, value: Word); + +function LandPixelGet(y, x: LongInt): Longword; +procedure LandPixelSet(y, x: LongInt; value: Longword); +function LandPixelRow(row: LongInt): PLongwordArray; + +var gameField: pointer; + implementation uses uUtils, uConsts, uVariables, uTypes; +{$linklib hwengine_future} + +function create_empty_game_field(width, height: Longword): pointer; cdecl; external; +procedure get_game_field_parameters(game_field: pointer; var width: LongInt; var height: LongInt; var play_width: LongInt; var play_height: LongInt); cdecl; external; +procedure dispose_game_field(game_field: pointer); cdecl; external; + +function land_get(game_field: pointer; x, y: LongInt): Word; cdecl; external; +procedure land_set(game_field: pointer; x, y: LongInt; value: Word); cdecl; external; +function land_row(game_field: pointer; row: LongInt): PWordArray; cdecl; external; +procedure land_fill(game_field: pointer; x, y: LongInt; border, fill: Word); cdecl; external; + +function land_pixel_get(game_field: pointer; x, y: LongInt): Longword; cdecl; external; +procedure land_pixel_set(game_field: pointer; x, y: LongInt; value: Longword); cdecl; external; +function land_pixel_row(game_field: pointer; row: LongInt): PLongwordArray; cdecl; external; + +function generate_outline_templated_game_field(feature_size: Longword; seed, template_type, data_path: PChar): pointer; cdecl; external; +function generate_wfc_templated_game_field(feature_size: Longword; seed, template_type, data_path: PChar): pointer; cdecl; external; +function generate_maze_game_field(feature_size: Longword; seed, template_type, data_path: PChar): pointer; cdecl; external; +procedure apply_theme(game_field: pointer; data_path, theme_name: PChar); cdecl; external; + +function LandGet(y, x: LongInt): Word; +begin + LandGet:= land_get(gameField, x, y) +end; + +procedure LandSet(y, x: LongInt; value: Word); +begin + land_set(gameField, x, y, value) +end; + +function LandRow(row: LongInt): PWordArray; +begin + LandRow:= land_row(gameField, row) +end; + +procedure FillLand(x, y: LongInt; border, value: Word); +begin + land_fill(gameField, x, y, border, value) +end; + +function LandPixelGet(y, x: LongInt): Longword; +begin + LandPixelGet:= land_pixel_get(gameField, x, y) +end; + +procedure LandPixelSet(y, x: LongInt; value: Longword); +begin + land_pixel_set(gameField, x, y, value) +end; + +function LandPixelRow(row: LongInt): PLongwordArray; +begin + LandPixelRow:= land_pixel_row(gameField, row) +end; + +procedure GenerateOutlineTemplatedLand(featureSize: Longword; seed, templateType: shortstring; dataPath: ansistring); +begin + seed[byte(seed[0]) + 1]:= #0; + templateType[byte(templateType[0]) + 1]:= #0; + + gameField:= generate_outline_templated_game_field(featureSize, @seed[1], @templateType[1], PChar(dataPath)); + get_game_field_parameters(gameField, LAND_WIDTH, LAND_HEIGHT, playWidth, playHeight); + + MaxHedgehogs:= 32; + hasGirders:= true; + + leftX:= (LAND_WIDTH - playWidth) div 2; + rightX:= Pred(leftX + playWidth); + topY:= LAND_HEIGHT - playHeight; + cWaterLine:= LAND_HEIGHT; + + // let's assume those are powers of two + LAND_WIDTH_MASK:= not(LAND_WIDTH-1); + LAND_HEIGHT_MASK:= not(LAND_HEIGHT-1); + + SetLength(LandDirty, (LAND_HEIGHT div 32), (LAND_WIDTH div 32)); + + initScreenSpaceVars(); +end; + +procedure GenerateWfcTemplatedLand(featureSize: Longword; seed, templateType: shortstring; dataPath: ansistring); +begin + seed[byte(seed[0]) + 1]:= #0; + templateType[byte(templateType[0]) + 1]:= #0; + + gameField:= generate_wfc_templated_game_field(featureSize, @seed[1], @templateType[1], PChar(dataPath)); + get_game_field_parameters(gameField, LAND_WIDTH, LAND_HEIGHT, playWidth, playHeight); + + MaxHedgehogs:= 32; + hasGirders:= true; + + leftX:= (LAND_WIDTH - playWidth) div 2; + rightX:= Pred(leftX + playWidth); + topY:= LAND_HEIGHT - playHeight; + cWaterLine:= LAND_HEIGHT; + + // let's assume those are powers of two + LAND_WIDTH_MASK:= not(LAND_WIDTH-1); + LAND_HEIGHT_MASK:= not(LAND_HEIGHT-1); + + SetLength(LandDirty, (LAND_HEIGHT div 32), (LAND_WIDTH div 32)); + + initScreenSpaceVars(); +end; + +procedure GenerateMazeLand(featureSize: Longword; seed, templateType: shortstring; dataPath: ansistring); +begin + seed[byte(seed[0]) + 1]:= #0; + templateType[byte(templateType[0]) + 1]:= #0; + + gameField:= generate_maze_game_field(featureSize, @seed[1], @templateType[1], PChar(dataPath)); + get_game_field_parameters(gameField, LAND_WIDTH, LAND_HEIGHT, playWidth, playHeight); + + MaxHedgehogs:= 32; + hasGirders:= true; + + leftX:= (LAND_WIDTH - playWidth) div 2; + rightX:= Pred(leftX + playWidth); + topY:= LAND_HEIGHT - playHeight; + cWaterLine:= LAND_HEIGHT; + + // let's assume those are powers of two + LAND_WIDTH_MASK:= not(LAND_WIDTH-1); + LAND_HEIGHT_MASK:= not(LAND_HEIGHT-1); + + SetLength(LandDirty, (LAND_HEIGHT div 32), (LAND_WIDTH div 32)); + + initScreenSpaceVars(); +end; + procedure ResizeLand(width, height: LongWord); var potW, potH: LongInt; begin @@ -19,12 +166,8 @@ LAND_WIDTH_MASK:= not(LAND_WIDTH-1); LAND_HEIGHT_MASK:= not(LAND_HEIGHT-1); cWaterLine:= LAND_HEIGHT; - if (cReducedQuality and rqBlurryLand) = 0 then - SetLength(LandPixels, LAND_HEIGHT, LAND_WIDTH) - else - SetLength(LandPixels, LAND_HEIGHT div 2, LAND_WIDTH div 2); - SetLength(Land, LAND_HEIGHT, LAND_WIDTH); + gameField:= create_empty_game_field(LAND_WIDTH, LAND_HEIGHT); SetLength(LandDirty, (LAND_HEIGHT div 32), (LAND_WIDTH div 32)); // 0.5 is already approaching on unplayable if (width div 4096 >= 2) or (height div 2048 >= 2) then cMaxZoomLevel:= cMaxZoomLevel/2; @@ -33,6 +176,11 @@ initScreenSpaceVars(); end; +procedure DisposeLand(); +begin + dispose_game_field(gameField) +end; + procedure InitWorldEdges(); var cy, cx, lx, ly: LongInt; found: boolean; @@ -70,7 +218,7 @@ for cx:= 0 to lx do begin for cy:= ly downto 0 do - if Land[cy, cx] <> 0 then + if LandGet(cy, cx) <> 0 then begin leftX:= max(0, cx - cWorldEdgeDist); // break out of both loops @@ -85,7 +233,7 @@ for cx:= lx downto 0 do begin for cy:= ly downto 0 do - if Land[cy, cx] <> 0 then + if LandGet(cy, cx) <> 0 then begin rightX:= min(lx, cx + cWorldEdgeDist); // break out of both loops diff -r 2b4f361e3891 -r 629d5123a979 hedgewars/uMisc.pas --- a/hedgewars/uMisc.pas Sat Sep 28 22:27:13 2024 +0200 +++ b/hedgewars/uMisc.pas Tue Dec 31 15:19:43 2024 +0100 @@ -29,10 +29,10 @@ function doSurfaceConversion(tmpsurf: PSDL_Surface): PSDL_Surface; function MakeScreenshot(filename: shortstring; k: LongInt; dump: LongWord): boolean; function GetTeamStatString(p: PTeam): shortstring; -function SDL_RectMake(x, y, width, height: LongInt): TSDL_Rect; inline; +function SDL_RectMake(x, y, width, height: LongInt): TSDL_Rect; implementation -uses uVariables, uUtils +uses uVariables, uUtils, uLandUtils {$IFDEF PNG_SCREENSHOTS}, PNGh, png {$ENDIF}; type PScreenshot = ^TScreenshot; @@ -288,18 +288,18 @@ for y:= 0 to LAND_HEIGHT-1 do for x:= 0 to LAND_WIDTH-1 do if dump = 2 then - PLongWordArray(p)^[y*LAND_WIDTH+x]:= LandPixels[LAND_HEIGHT-1-y, x] + PLongWordArray(p)^[y*LAND_WIDTH+x]:= LandPixelGet(LAND_HEIGHT-1-y, x) else begin - if Land[LAND_HEIGHT-1-y, x] and lfIndestructible = lfIndestructible then + if LandGet(LAND_HEIGHT-1-y, x) and lfIndestructible = lfIndestructible then PLongWordArray(p)^[y*LAND_WIDTH+x]:= (AMask or RMask) - else if Land[LAND_HEIGHT-1-y, x] and lfIce = lfIce then + else if LandGet(LAND_HEIGHT-1-y, x) and lfIce = lfIce then PLongWordArray(p)^[y*LAND_WIDTH+x]:= (AMask or BMask) - else if Land[LAND_HEIGHT-1-y, x] and lfBouncy = lfBouncy then + else if LandGet(LAND_HEIGHT-1-y, x) and lfBouncy = lfBouncy then PLongWordArray(p)^[y*LAND_WIDTH+x]:= (AMask or GMask) - else if Land[LAND_HEIGHT-1-y, x] and lfObject = lfObject then + else if LandGet(LAND_HEIGHT-1-y, x) and lfObject = lfObject then PLongWordArray(p)^[y*LAND_WIDTH+x]:= $FFFFFFFF - else if Land[LAND_HEIGHT-1-y, x] and lfBasic = lfBasic then + else if LandGet(LAND_HEIGHT-1-y, x) and lfBasic = lfBasic then PLongWordArray(p)^[y*LAND_WIDTH+x]:= AMask else PLongWordArray(p)^[y*LAND_WIDTH+x]:= 0 @@ -353,7 +353,7 @@ end; end; -function SDL_RectMake(x, y, width, height: LongInt): TSDL_Rect; inline; +function SDL_RectMake(x, y, width, height: LongInt): TSDL_Rect; begin SDL_RectMake.x:= x; SDL_RectMake.y:= y; diff -r 2b4f361e3891 -r 629d5123a979 hedgewars/uRandom.pas --- a/hedgewars/uRandom.pas Sat Sep 28 22:27:13 2024 +0200 +++ b/hedgewars/uRandom.pas Tue Dec 31 15:19:43 2024 +0100 @@ -32,8 +32,8 @@ procedure SetRandomSeed(Seed: shortstring; dropAdditionalPart: boolean); // Sets the seed that should be used for generating pseudo-random values. function GetRandomf: hwFloat; // Returns a pseudo-random hwFloat. -function GetRandom(m: LongWord): LongWord; inline; // Returns a positive pseudo-random integer smaller than m. -procedure AddRandomness(r: LongWord); inline; +function GetRandom(m: LongWord): LongWord; // Returns a positive pseudo-random integer smaller than m. +procedure AddRandomness(r: LongWord); function rndSign(num: hwFloat): hwFloat; // Returns num with a random chance of having a inverted sign. @@ -42,13 +42,13 @@ var cirbuf: array[0..63] of Longword; n: byte; -procedure AddRandomness(r: LongWord); inline; +procedure AddRandomness(r: LongWord); begin n:= (n + 1) and $3F; cirbuf[n]:= cirbuf[n] xor r; end; -function GetNext: Longword; inline; +function GetNext: Longword; begin n:= (n + 1) and $3F; cirbuf[n]:= @@ -90,7 +90,7 @@ GetRandomf.QWordValue:= GetNext end; -function GetRandom(m: LongWord): LongWord; inline; +function GetRandom(m: LongWord): LongWord; begin GetNext; GetRandom:= GetNext mod m diff -r 2b4f361e3891 -r 629d5123a979 hedgewars/uRender.pas --- a/hedgewars/uRender.pas Sat Sep 28 22:27:13 2024 +0200 +++ b/hedgewars/uRender.pas Tue Dec 31 15:19:43 2024 +0100 @@ -30,18 +30,18 @@ procedure DrawSprite (Sprite: TSprite; X, Y, Frame: LongInt); procedure DrawSprite (Sprite: TSprite; X, Y, FrameX, FrameY: LongInt); -procedure DrawSpriteFromRect (Sprite: TSprite; r: TSDL_Rect; X, Y, Height, Position: LongInt); inline; +procedure DrawSpriteFromRect (Sprite: TSprite; r: TSDL_Rect; X, Y, Height, Position: LongInt); procedure DrawSpriteClipped (Sprite: TSprite; X, Y, TopY, RightX, BottomY, LeftX: LongInt); procedure DrawSpriteRotated (Sprite: TSprite; X, Y, Dir: LongInt; Angle: real); procedure DrawSpriteRotatedF (Sprite: TSprite; X, Y, Frame, Dir: LongInt; Angle: real); procedure DrawSpriteRotatedFReal(Sprite: TSprite; X, Y: Real; Frame, Dir: LongInt; Angle: real); procedure DrawSpritePivotedF(Sprite: TSprite; X, Y, Frame, Dir, PivotX, PivotY: LongInt; Angle: real); -procedure DrawTexture (X, Y: LongInt; Texture: PTexture); inline; +procedure DrawTexture (X, Y: LongInt; Texture: PTexture); procedure DrawTexture (X, Y: LongInt; Texture: PTexture; Scale: GLfloat); procedure DrawTexture2 (X, Y: LongInt; Texture: PTexture; Scale, Overlap: GLfloat); -procedure DrawTextureFromRect (X, Y: LongInt; r: PSDL_Rect; SourceTexture: PTexture); inline; -procedure DrawTextureFromRect (X, Y, W, H: LongInt; r: PSDL_Rect; SourceTexture: PTexture); inline; +procedure DrawTextureFromRect (X, Y: LongInt; r: PSDL_Rect; SourceTexture: PTexture); +procedure DrawTextureFromRect (X, Y, W, H: LongInt; r: PSDL_Rect; SourceTexture: PTexture); procedure DrawTextureFromRectDir(X, Y, W, H: LongInt; r: PSDL_Rect; SourceTexture: PTexture; Dir: LongInt); procedure DrawTextureCentered (X, Top: LongInt; Source: PTexture); procedure DrawTextureF (Texture: PTexture; Scale: GLfloat; X, Y, Frame, Dir, w, h: LongInt); @@ -53,9 +53,9 @@ procedure DrawCircle (X, Y, Radius, Width: LongInt; color: LongWord); procedure DrawCircleFilled (X, Y, Radius: LongInt; r, g, b, a: Byte); -procedure DrawLine (X0, Y0, X1, Y1, Width: Single; color: LongWord); inline; +procedure DrawLine (X0, Y0, X1, Y1, Width: Single; color: LongWord); procedure DrawLine (X0, Y0, X1, Y1, Width: Single; r, g, b, a: Byte); -procedure DrawLineWrapped (X0, Y0, X1, Y1, Width: Single; goesLeft: boolean; Wraps: LongWord; color: LongWord); inline; +procedure DrawLineWrapped (X0, Y0, X1, Y1, Width: Single; goesLeft: boolean; Wraps: LongWord; color: LongWord); procedure DrawLineWrapped (X0, Y0, X1, Y1, Width: Single; goesLeft: boolean; Wraps: LongWord; r, g, b, a: Byte); procedure DrawLineOnScreen (X0, Y0, X1, Y1, Width: Single; r, g, b, a: Byte); procedure DrawRect (rect: TSDL_Rect; r, g, b, a: Byte; Fill: boolean); @@ -70,19 +70,19 @@ {$ENDIF} procedure RenderSetClearColor (r, g, b, a: real); procedure Tint (r, g, b, a: Byte); -procedure Tint (c: Longword); inline; -procedure untint(); inline; -procedure setTintAdd (enable: boolean); inline; +procedure Tint (c: Longword); +procedure untint(); +procedure setTintAdd (enable: boolean); // call this to finish the rendering of current frame procedure FinishRender(); -function isAreaOffscreen(X, Y, Width, Height: LongInt): boolean; inline; -function isCircleOffscreen(X, Y, RadiusSquared: LongInt): boolean; inline; +function isAreaOffscreen(X, Y, Width, Height: LongInt): boolean; +function isCircleOffscreen(X, Y, RadiusSquared: LongInt): boolean; // 0 => not offscreen, <0 => left/top of screen >0 => right/below of screen -function isDxAreaOffscreen(X, Width: LongInt): LongInt; inline; -function isDyAreaOffscreen(Y, Height: LongInt): LongInt; inline; +function isDxAreaOffscreen(X, Width: LongInt): LongInt; +function isDyAreaOffscreen(Y, Height: LongInt): LongInt; procedure SetScale(f: GLfloat); procedure UpdateViewLimits(); @@ -97,15 +97,15 @@ procedure EnableTexture(enable:Boolean); -procedure SetTexCoordPointer(p: Pointer;n: Integer); inline; -procedure SetVertexPointer(p: Pointer;n: Integer); inline; -procedure SetColorPointer(p: Pointer;n: Integer); inline; +procedure SetTexCoordPointer(p: Pointer;n: Integer); +procedure SetVertexPointer(p: Pointer;n: Integer); +procedure SetColorPointer(p: Pointer;n: Integer); -procedure UpdateModelviewProjection(); inline; +procedure UpdateModelviewProjection(); -procedure openglPushMatrix (); inline; -procedure openglPopMatrix (); inline; -procedure openglTranslatef (X, Y, Z: GLfloat); inline; +procedure openglPushMatrix (); +procedure openglPopMatrix (); +procedure openglTranslatef (X, Y, Z: GLfloat); implementation @@ -147,12 +147,12 @@ procedure DeleteFramebuffer(var frame, depth, tex: GLuint); forward; {$ENDIF} -function isAreaOffscreen(X, Y, Width, Height: LongInt): boolean; inline; +function isAreaOffscreen(X, Y, Width, Height: LongInt): boolean; begin isAreaOffscreen:= (isDxAreaOffscreen(X, Width) <> 0) or (isDyAreaOffscreen(Y, Height) <> 0); end; -function isCircleOffscreen(X, Y, RadiusSquared: LongInt): boolean; inline; +function isCircleOffscreen(X, Y, RadiusSquared: LongInt): boolean; var dRightX, dBottomY, dLeftX, dTopY: LongInt; begin dRightX:= (X - ViewRightX); @@ -166,14 +166,14 @@ ((dTopY > 0) and (sqr(dTopY) > RadiusSquared)) end; -function isDxAreaOffscreen(X, Width: LongInt): LongInt; inline; +function isDxAreaOffscreen(X, Width: LongInt): LongInt; begin if X > ViewRightX then exit(1); if X + Width < ViewLeftX then exit(-1); isDxAreaOffscreen:= 0; end; -function isDyAreaOffscreen(Y, Height: LongInt): LongInt; inline; +function isDyAreaOffscreen(Y, Height: LongInt): LongInt; begin if Y > ViewBottomY then exit(1); if Y + Height < ViewTopY then exit(-1); @@ -658,7 +658,7 @@ // disable/lower perspective correction (will not need it anyway) end; -procedure openglLoadIdentity(); inline; +procedure openglLoadIdentity(); begin {$IFDEF GL2} hglLoadIdentity(); @@ -667,7 +667,7 @@ {$ENDIF} end; -procedure openglTranslProjMatrix(X, Y, Z: GLfloat); inline; +procedure openglTranslProjMatrix(X, Y, Z: GLfloat); begin {$IFDEF GL2} hglMatrixMode(MATRIX_PROJECTION); @@ -680,7 +680,7 @@ {$ENDIF} end; -procedure openglPushMatrix(); inline; +procedure openglPushMatrix(); begin {$IFDEF GL2} hglPushMatrix(); @@ -689,7 +689,7 @@ {$ENDIF} end; -procedure openglPopMatrix(); inline; +procedure openglPopMatrix(); begin {$IFDEF GL2} hglPopMatrix(); @@ -698,7 +698,7 @@ {$ENDIF} end; -procedure openglTranslatef(X, Y, Z: GLfloat); inline; +procedure openglTranslatef(X, Y, Z: GLfloat); begin {$IFDEF GL2} hglTranslatef(X, Y, Z); @@ -707,7 +707,7 @@ {$ENDIF} end; -procedure openglScalef(ScaleX, ScaleY, ScaleZ: GLfloat); inline; +procedure openglScalef(ScaleX, ScaleY, ScaleZ: GLfloat); begin {$IFDEF GL2} hglScalef(ScaleX, ScaleY, ScaleZ); @@ -716,7 +716,7 @@ {$ENDIF} end; -procedure openglRotatef(RotX, RotY, RotZ: GLfloat; dir: LongInt); inline; +procedure openglRotatef(RotX, RotY, RotZ: GLfloat; dir: LongInt); { workaround for pascal bug https://bugs.freepascal.org/view.php?id=27222 } var tmpdir: LongInt; begin @@ -728,7 +728,7 @@ {$ENDIF} end; -procedure openglUseColorOnly(b :boolean); inline; +procedure openglUseColorOnly(b :boolean); begin if b then begin @@ -755,7 +755,7 @@ EnableTexture(not b); end; -procedure UpdateModelviewProjection(); inline; +procedure UpdateModelviewProjection(); {$IFDEF GL2} var mvp: TMatrix4x4f; @@ -770,7 +770,7 @@ {$ENDIF} end; -procedure SetTexCoordPointer(p: Pointer; n: Integer); inline; +procedure SetTexCoordPointer(p: Pointer; n: Integer); begin {$IFDEF GL2} glBindBuffer(GL_ARRAY_BUFFER, tBuffer); @@ -786,7 +786,7 @@ {$ENDIF} end; -procedure SetVertexPointer(p: Pointer; n: Integer); inline; +procedure SetVertexPointer(p: Pointer; n: Integer); begin {$IFDEF GL2} glBindBuffer(GL_ARRAY_BUFFER, vBuffer); @@ -802,7 +802,7 @@ {$ENDIF} end; -procedure SetColorPointer(p: Pointer; n: Integer); inline; +procedure SetColorPointer(p: Pointer; n: Integer); begin {$IFDEF GL2} glBindBuffer(GL_ARRAY_BUFFER, cBuffer); @@ -887,19 +887,19 @@ UpdateModelviewProjection; end; -procedure DrawSpriteFromRect(Sprite: TSprite; r: TSDL_Rect; X, Y, Height, Position: LongInt); inline; +procedure DrawSpriteFromRect(Sprite: TSprite; r: TSDL_Rect; X, Y, Height, Position: LongInt); begin r.y:= r.y + Height * Position; r.h:= Height; DrawTextureFromRect(X, Y, @r, SpritesData[Sprite].Texture) end; -procedure DrawTextureFromRect(X, Y: LongInt; r: PSDL_Rect; SourceTexture: PTexture); inline; +procedure DrawTextureFromRect(X, Y: LongInt; r: PSDL_Rect; SourceTexture: PTexture); begin DrawTextureFromRectDir(X, Y, r^.w, r^.h, r, SourceTexture, 1) end; -procedure DrawTextureFromRect(X, Y, W, H: LongInt; r: PSDL_Rect; SourceTexture: PTexture); inline; +procedure DrawTextureFromRect(X, Y, W, H: LongInt; r: PSDL_Rect; SourceTexture: PTexture); begin DrawTextureFromRectDir(X, Y, W, H, r, SourceTexture, 1) end; @@ -967,7 +967,7 @@ end; -procedure DrawTexture(X, Y: LongInt; Texture: PTexture); inline; +procedure DrawTexture(X, Y: LongInt; Texture: PTexture); begin DrawTexture(X, Y, Texture, 1.0); end; @@ -1375,7 +1375,7 @@ end; // Same as below, but with color as LongWord -procedure DrawLine(X0, Y0, X1, Y1, Width: Single; color: LongWord); inline; +procedure DrawLine(X0, Y0, X1, Y1, Width: Single; color: LongWord); begin DrawLine(X0, Y0, X1, Y1, Width, (color shr 24) and $FF, (color shr 16) and $FF, (color shr 8) and $FF, color and $FF) end; @@ -1400,7 +1400,7 @@ end; // Same as below, but with color as a longword -procedure DrawLineWrapped(X0, Y0, X1, Y1, Width: Single; goesLeft: boolean; Wraps: LongWord; color: LongWord); inline; +procedure DrawLineWrapped(X0, Y0, X1, Y1, Width: Single; goesLeft: boolean; Wraps: LongWord; color: LongWord); begin DrawLineWrapped(X0, Y0, X1, Y1, Width, goesLeft, Wraps, (color shr 24) and $FF, (color shr 16) and $FF, (color shr 8) and $FF, color and $FF); end; @@ -2073,7 +2073,7 @@ end; -procedure openglTint(r, g, b, a: Byte); inline; +procedure openglTint(r, g, b, a: Byte); {$IFDEF GL2} const scale:Real = 1.0/255.0; @@ -2109,20 +2109,20 @@ LastTint:= nc; end; -procedure Tint(c: Longword); inline; +procedure Tint(c: Longword); begin if c = LastTint then exit; Tint(((c shr 24) and $FF), ((c shr 16) and $FF), (c shr 8) and $FF, (c and $FF)) end; -procedure untint(); inline; +procedure untint(); begin if cWhiteColor = LastTint then exit; openglTint($FF, $FF, $FF, $FF); LastTint:= cWhiteColor; end; -procedure setTintAdd(enable: boolean); inline; +procedure setTintAdd(enable: boolean); begin {$IFDEF GL2} if enable then diff -r 2b4f361e3891 -r 629d5123a979 hedgewars/uRenderUtils.pas --- a/hedgewars/uRenderUtils.pas Sat Sep 28 22:27:13 2024 +0200 +++ b/hedgewars/uRenderUtils.pas Tue Dec 31 15:19:43 2024 +0100 @@ -26,13 +26,13 @@ procedure flipSurface(Surface: PSDL_Surface; Vertical: Boolean); procedure copyRotatedSurface(src, dest: PSDL_Surface); // this is necessary since width/height are read only in SDL -procedure copyToXY(src, dest: PSDL_Surface; destX, destY: LongInt); inline; +procedure copyToXY(src, dest: PSDL_Surface; destX, destY: LongInt); procedure copyToXYFromRect(src, dest: PSDL_Surface; srcX, srcY, srcW, srcH, destX, destY: LongInt); function GetSurfaceFrameCoordinateX(Surface: PSDL_Surface; Frame, frameWidth, frameHeight: LongInt): LongInt; function GetSurfaceFrameCoordinateY(Surface: PSDL_Surface; Frame, frameHeight: LongInt): LongInt; -procedure DrawSprite2Surf(sprite: TSprite; dest: PSDL_Surface; x,y: LongInt); inline; +procedure DrawSprite2Surf(sprite: TSprite; dest: PSDL_Surface; x,y: LongInt); procedure DrawSpriteFrame2Surf(sprite: TSprite; dest: PSDL_Surface; x,y: LongInt; frame: LongInt); procedure DrawLine2Surf(dest: PSDL_Surface; x0,y0,x1,y1:LongInt; r,g,b: byte); procedure DrawRoundRect(rect: PSDL_Rect; BorderColor, FillColor: Longword; Surface: PSDL_Surface; Clear: boolean); @@ -41,7 +41,7 @@ function RenderStringTexLim(s: ansistring; Color: Longword; font: THWFont; maxLength: LongWord): PTexture; function RenderSpeechBubbleTex(s: ansistring; SpeechType: Longword; font: THWFont): PTexture; -function IsTooDarkToRead(TextColor: Longword): boolean; inline; +function IsTooDarkToRead(TextColor: Longword): boolean; implementation uses uVariables, uConsts, uTextures, SysUtils, uUtils, uDebug; @@ -99,7 +99,7 @@ GetSurfaceFrameCoordinateY:= (Frame mod ny) * frameHeight; end; -function IsTooDarkToRead(TextColor: LongWord): boolean; inline; +function IsTooDarkToRead(TextColor: LongWord): boolean; var clr: TSDL_Color; begin clr.r:= (TextColor shr 16) and $FF; @@ -178,7 +178,7 @@ SDL_UnlockSurface(Surface); end; -procedure copyToXY(src, dest: PSDL_Surface; destX, destY: LongInt); inline; +procedure copyToXY(src, dest: PSDL_Surface; destX, destY: LongInt); begin // copy from complete src copyToXYFromRect(src, dest, 0, 0, src^.w, src^.h, destX, destY); @@ -254,7 +254,7 @@ SDL_UnlockSurface(dest); end; -procedure DrawSprite2Surf(sprite: TSprite; dest: PSDL_Surface; x,y: LongInt); inline; +procedure DrawSprite2Surf(sprite: TSprite; dest: PSDL_Surface; x,y: LongInt); begin DrawSpriteFrame2Surf(sprite, dest, x, y, 0); end; diff -r 2b4f361e3891 -r 629d5123a979 hedgewars/uScript.pas --- a/hedgewars/uScript.pas Sat Sep 28 22:27:13 2024 +0200 +++ b/hedgewars/uScript.pas Tue Dec 31 15:19:43 2024 +0100 @@ -184,14 +184,14 @@ LuaError('-- SYNTAX: ' + call + ' ( ' + paramsyntax + ' )'); end; -procedure LuaParameterCountError(expected, call, paramsyntax: shortstring; wrongcount: LongInt); inline; +procedure LuaParameterCountError(expected, call, paramsyntax: shortstring; wrongcount: LongInt); begin // TODO: i18n? LuaCallError('Wrong number of parameters! (is: ' + inttostr(wrongcount) + ', should be: '+ expected + ')', call, paramsyntax); end; // compare with allowed count -function CheckLuaParamCount(L : Plua_State; count: LongInt; call, paramsyntax: shortstring): boolean; inline; +function CheckLuaParamCount(L : Plua_State; count: LongInt; call, paramsyntax: shortstring): boolean; var c: LongInt; begin c:= lua_gettop(L); @@ -205,7 +205,7 @@ end; // check if is either count1 or count2 -function CheckAndFetchParamCount(L : Plua_State; count1, count2: LongInt; call, paramsyntax: shortstring; out actual: LongInt): boolean; inline; +function CheckAndFetchParamCount(L : Plua_State; count1, count2: LongInt; call, paramsyntax: shortstring; out actual: LongInt): boolean; begin actual:= lua_gettop(L); if (actual <> count1) and (actual <> count2) then @@ -218,7 +218,7 @@ end; // check if is in range of count1 and count2 -function CheckAndFetchParamCountRange(L : Plua_State; count1, count2: LongInt; call, paramsyntax: shortstring; out actual: LongInt): boolean; inline; +function CheckAndFetchParamCountRange(L : Plua_State; count1, count2: LongInt; call, paramsyntax: shortstring; out actual: LongInt): boolean; begin actual:= lua_gettop(L); if (actual < count1) or (actual > count2) then @@ -231,7 +231,7 @@ end; // check if is same or higher as minCount -function CheckAndFetchLuaParamMinCount(L : Plua_State; minCount: LongInt; call, paramsyntax: shortstring; out actual: LongInt): boolean; inline; +function CheckAndFetchLuaParamMinCount(L : Plua_State; minCount: LongInt; call, paramsyntax: shortstring; out actual: LongInt): boolean; begin actual:= lua_gettop(L); if (actual < minCount) then @@ -243,7 +243,7 @@ CheckAndFetchLuaParamMinCount:= true; end; -function LuaToGearTypeOrd(L : Plua_State; i: LongInt; call, paramsyntax: shortstring): LongInt; inline; +function LuaToGearTypeOrd(L : Plua_State; i: LongInt; call, paramsyntax: shortstring): LongInt; begin if lua_isnoneornil(L, i) then i:= -1 else i:= Trunc(lua_tonumber(L, i)); @@ -256,7 +256,7 @@ LuaToGearTypeOrd:= i; end; -function LuaToVisualGearTypeOrd(L : Plua_State; i: LongInt; call, paramsyntax: shortstring): LongInt; inline; +function LuaToVisualGearTypeOrd(L : Plua_State; i: LongInt; call, paramsyntax: shortstring): LongInt; begin if lua_isnoneornil(L, i) then i:= -1 else i:= Trunc(lua_tonumber(L, i)); @@ -269,7 +269,7 @@ LuaToVisualGearTypeOrd:= i; end; -function LuaToAmmoTypeOrd(L : Plua_State; i: LongInt; call, paramsyntax: shortstring): LongInt; inline; +function LuaToAmmoTypeOrd(L : Plua_State; i: LongInt; call, paramsyntax: shortstring): LongInt; begin if lua_isnoneornil(L, i) then i:= -1 else i:= Trunc(lua_tonumber(L, i)); @@ -282,7 +282,7 @@ LuaToAmmoTypeOrd:= i; end; -function LuaToStatInfoTypeOrd(L : Plua_State; i: LongInt; call, paramsyntax: shortstring): LongInt; inline; +function LuaToStatInfoTypeOrd(L : Plua_State; i: LongInt; call, paramsyntax: shortstring): LongInt; begin if lua_isnoneornil(L, i) then i:= -1 else i:= Trunc(lua_tonumber(L, i)); @@ -295,7 +295,7 @@ LuaToStatInfoTypeOrd:= i; end; -function LuaToSoundOrd(L : Plua_State; i: LongInt; call, paramsyntax: shortstring): LongInt; inline; +function LuaToSoundOrd(L : Plua_State; i: LongInt; call, paramsyntax: shortstring): LongInt; begin if lua_isnoneornil(L, i) then i:= -1 else i:= Trunc(lua_tonumber(L, i)); @@ -334,7 +334,7 @@ LuaToGoalStrIdOrd:= i; end; -function LuaToHogEffectOrd(L : Plua_State; i: LongInt; call, paramsyntax: shortstring): LongInt; inline; +function LuaToHogEffectOrd(L : Plua_State; i: LongInt; call, paramsyntax: shortstring): LongInt; begin if lua_isnoneornil(L, i) then i:= -1 else i:= Trunc(lua_tonumber(L, i)); @@ -347,7 +347,7 @@ LuaToHogEffectOrd:= i; end; -function LuaToCapGroupOrd(L : Plua_State; i: LongInt; call, paramsyntax: shortstring): LongInt; inline; +function LuaToCapGroupOrd(L : Plua_State; i: LongInt; call, paramsyntax: shortstring): LongInt; begin if lua_isnoneornil(L, i) then i:= -1 else i:= Trunc(lua_tonumber(L, i)); @@ -360,7 +360,7 @@ LuaToCapGroupOrd:= i; end; -function LuaToSpriteOrd(L : Plua_State; i: LongInt; call, paramsyntax: shortstring): LongInt; inline; +function LuaToSpriteOrd(L : Plua_State; i: LongInt; call, paramsyntax: shortstring): LongInt; begin if lua_isnoneornil(L, i) then i:= -1 else i:= Trunc(lua_tonumber(L, i)); @@ -373,7 +373,7 @@ LuaToSpriteOrd:= i; end; -function LuaToMapGenOrd(L : Plua_State; i: LongInt; call, paramsyntax: shortstring): LongInt; inline; +function LuaToMapGenOrd(L : Plua_State; i: LongInt; call, paramsyntax: shortstring): LongInt; begin if lua_isnoneornil(L, i) then i:= -1 else i:= Trunc(lua_tonumber(L, i)); diff -r 2b4f361e3891 -r 629d5123a979 hedgewars/uStore.pas --- a/hedgewars/uStore.pas Sat Sep 28 22:27:13 2024 +0200 +++ b/hedgewars/uStore.pas Tue Dec 31 15:19:43 2024 +0100 @@ -59,7 +59,7 @@ procedure InitOffscreenOpenGL; {$ENDIF} -procedure WarpMouse(x, y: Word); inline; +procedure WarpMouse(x, y: Word); procedure SwapBuffers; {$IFDEF USE_VIDEO_RECORDING}cdecl{$ELSE}inline{$ENDIF}; procedure SetSkyColor(r, g, b: real); @@ -1381,7 +1381,7 @@ // for sdl2 we provide a SDL_WarpMouse() which just calls this function // this has the advantage of reducing 'uses' and 'ifdef' statements // (SDLwindow is a private member of this module) -procedure WarpMouse(x, y: Word); inline; +procedure WarpMouse(x, y: Word); begin SDL_WarpMouseInWindow(SDLwindow, x, y); end; diff -r 2b4f361e3891 -r 629d5123a979 hedgewars/uTextures.pas --- a/hedgewars/uTextures.pas Sat Sep 28 22:27:13 2024 +0200 +++ b/hedgewars/uTextures.pas Tue Dec 31 15:19:43 2024 +0100 @@ -26,14 +26,14 @@ procedure Surface2GrayScale(surf: PSDL_Surface); function Surface2Tex(surf: PSDL_Surface; enableClamp: boolean): PTexture; procedure PrettifySurfaceAlpha(surf: PSDL_Surface; pixels: PLongwordArray); -procedure PrettifyAlpha2D(pixels: TLandArray; height, width: LongWord); +procedure PrettifyAlpha2D(height, width: LongWord); procedure FreeAndNilTexture(var tex: PTexture); procedure initModule; procedure freeModule; implementation -uses GLunit, uUtils, uVariables, uConsts, uDebug, uConsole; +uses GLunit, uUtils, uVariables, uConsts, uDebug, uConsole, uLandUtils; var TextureList: PTexture; @@ -194,7 +194,7 @@ PrettifyAlpha(pixels, nil, si, li, w); end; -procedure PrettifyAlpha2D(pixels: TLandArray; height, width: LongWord); +procedure PrettifyAlpha2D(height, width: LongWord); var // current y; last x, second last y of array; y, lx, sly: LongWord; @@ -203,10 +203,10 @@ lx:= width - 1; for y:= 0 to sly do begin - PrettifyAlpha(PLongWordArray(pixels[y]), PLongWordArray(pixels[y+1]), 0, lx, 0); + PrettifyAlpha(LandPixelRow(y), LandPixelRow(y+1), 0, lx, 0); end; // don't forget last row - PrettifyAlpha(PLongWordArray(pixels[sly+1]), nil, 0, lx, 0); + PrettifyAlpha(LandPixelRow(sly+1), nil, 0, lx, 0); end; function Surface2Tex(surf: PSDL_Surface; enableClamp: boolean): PTexture; diff -r 2b4f361e3891 -r 629d5123a979 hedgewars/uTouch.pas --- a/hedgewars/uTouch.pas Sat Sep 28 22:27:13 2024 +0200 +++ b/hedgewars/uTouch.pas Tue Dec 31 15:19:43 2024 +0100 @@ -613,7 +613,7 @@ fingerHasMoved := trunc(sqrt(sqr(finger.X-finger.historicalX) + sqr(finger.y-finger.historicalY))) > 30; end; -function calculateDelta(finger1, finger2: TTouch_Data): LongInt; inline; +function calculateDelta(finger1, finger2: TTouch_Data): LongInt; begin calculateDelta := Round(sqrt(sqr(finger2.x-finger1.x) + sqr(finger2.y-finger1.y))); end; diff -r 2b4f361e3891 -r 629d5123a979 hedgewars/uTypes.pas --- a/hedgewars/uTypes.pas Sat Sep 28 22:27:13 2024 +0200 +++ b/hedgewars/uTypes.pas Tue Dec 31 15:19:43 2024 +0100 @@ -99,7 +99,7 @@ // Gears that interact with other Gears and/or Land // first row of gears ( nil) and (cWindSpeedf<0) then exit(Lsprite) @@ -133,7 +133,7 @@ exit(sprite); end; -function GetSpriteData(sprite, SDsprite: TSprite): PSpriteData; inline; +function GetSpriteData(sprite, SDsprite: TSprite): PSpriteData; begin exit(@SpritesData[GetSprite(sprite, SDsprite)]); end; @@ -498,7 +498,7 @@ end; end; -procedure AddFlake; inline; +procedure AddFlake; begin AddVisualGear(cLeftScreenBorder + random(cScreenSpace), LAND_HEIGHT-cCloudOffset+ random(cCloudOffset), vgtFlake); end; diff -r 2b4f361e3891 -r 629d5123a979 hedgewars/uVisualGearsHandlers.pas --- a/hedgewars/uVisualGearsHandlers.pas Sat Sep 28 22:27:13 2024 +0200 +++ b/hedgewars/uVisualGearsHandlers.pas Tue Dec 31 15:19:43 2024 +0100 @@ -71,7 +71,7 @@ procedure doStepSmoothWindBar(Gear: PVisualGear; Steps: Longword); procedure doStepStraightShot(Gear: PVisualGear; Steps: Longword); -function isSorterActive: boolean; inline; +function isSorterActive: boolean; procedure initModule; implementation @@ -573,7 +573,7 @@ end; currsorter: PVisualGear = nil; -function isSorterActive: boolean; inline; +function isSorterActive: boolean; begin isSorterActive:= currsorter <> nil end; diff -r 2b4f361e3891 -r 629d5123a979 hedgewars/uVisualGearsList.pas --- a/hedgewars/uVisualGearsList.pas Sat Sep 28 22:27:13 2024 +0200 +++ b/hedgewars/uVisualGearsList.pas Tue Dec 31 15:19:43 2024 +0100 @@ -22,9 +22,9 @@ interface uses uTypes; -function AddVisualGear(X, Y: LongInt; Kind: TVisualGearType): PVisualGear; inline; -function AddVisualGear(X, Y: LongInt; Kind: TVisualGearType; State: LongWord): PVisualGear; inline; -function AddVisualGear(X, Y: LongInt; Kind: TVisualGearType; State: LongWord; Critical: Boolean): PVisualGear; inline; +function AddVisualGear(X, Y: LongInt; Kind: TVisualGearType): PVisualGear; +function AddVisualGear(X, Y: LongInt; Kind: TVisualGearType; State: LongWord): PVisualGear; +function AddVisualGear(X, Y: LongInt; Kind: TVisualGearType; State: LongWord; Critical: Boolean): PVisualGear; function AddVisualGear(X, Y: LongInt; Kind: TVisualGearType; State: LongWord; Critical: Boolean; Layer: LongInt): PVisualGear; procedure DeleteVisualGear(Gear: PVisualGear); function VisualGearByUID(uid : Longword) : PVisualGear; @@ -39,7 +39,7 @@ implementation uses uCollisions, uFloat, uVariables, uConsts, uTextures, uVisualGearsHandlers, uScript; -function AddVisualGear(X, Y: LongInt; Kind: TVisualGearType): PVisualGear; inline; +function AddVisualGear(X, Y: LongInt; Kind: TVisualGearType): PVisualGear; begin // adjust some visual gear types if underwater if CheckCoordInWater(X, Y) and ((Kind = vgtBeeTrace) or (Kind = vgtSmokeTrace) or (Kind = vgtEvilTrace)) then @@ -48,12 +48,12 @@ AddVisualGear:= AddVisualGear(X, Y, Kind, 0, false, -1); end; -function AddVisualGear(X, Y: LongInt; Kind: TVisualGearType; State: LongWord): PVisualGear; inline; +function AddVisualGear(X, Y: LongInt; Kind: TVisualGearType; State: LongWord): PVisualGear; begin AddVisualGear:= AddVisualGear(X, Y, Kind, State, false, -1); end; -function AddVisualGear(X, Y: LongInt; Kind: TVisualGearType; State: LongWord; Critical: Boolean): PVisualGear; inline; +function AddVisualGear(X, Y: LongInt; Kind: TVisualGearType; State: LongWord; Critical: Boolean): PVisualGear; begin AddVisualGear:= AddVisualGear(X, Y, Kind, State, Critical, -1); end; diff -r 2b4f361e3891 -r 629d5123a979 hedgewars/uWorld.pas --- a/hedgewars/uWorld.pas Sat Sep 28 22:27:13 2024 +0200 +++ b/hedgewars/uWorld.pas Tue Dec 31 15:19:43 2024 +0100 @@ -1165,14 +1165,14 @@ var preShiftWorldDx: LongInt; -procedure ShiftWorld(Dir: LongInt); inline; +procedure ShiftWorld(Dir: LongInt); begin preShiftWorldDx:= WorldDx; Dir := Dir * LongInt(playWidth); WorldDx:= WorldDx + Dir; end; -procedure UnshiftWorld(); inline; +procedure UnshiftWorld(); begin WorldDx:= preShiftWorldDx; end; diff -r 2b4f361e3891 -r 629d5123a979 project_files/hwc/CMakeLists.txt --- a/project_files/hwc/CMakeLists.txt Sat Sep 28 22:27:13 2024 +0200 +++ b/project_files/hwc/CMakeLists.txt Tue Dec 31 15:19:43 2024 +0100 @@ -119,6 +119,7 @@ physfs physlayer m + hwengine_future ${HW_LINK_LIBS} #TODO: add other libraries ) diff -r 2b4f361e3891 -r 629d5123a979 rust/fpnum/Cargo.toml --- a/rust/fpnum/Cargo.toml Sat Sep 28 22:27:13 2024 +0200 +++ b/rust/fpnum/Cargo.toml Tue Dec 31 15:19:43 2024 +0100 @@ -2,6 +2,7 @@ name = "fpnum" version = "0.1.0" authors = ["Andrey Korotaev "] -edition = "2018" +edition = "2021" [dependencies] +saturate = "0.1.0" diff -r 2b4f361e3891 -r 629d5123a979 rust/fpnum/src/lib.rs --- a/rust/fpnum/src/lib.rs Sat Sep 28 22:27:13 2024 +0200 +++ b/rust/fpnum/src/lib.rs Tue Dec 31 15:19:43 2024 +0100 @@ -1,4 +1,6 @@ -use std::{cmp, ops, ops::Shl}; +use std::{cmp, ops}; +use std::marker::PhantomData; +use saturate::SaturatingInto; const POSITIVE_MASK: u64 = 0x0000_0000_0000_0000; const NEGATIVE_MASK: u64 = 0xFFFF_FFFF_FFFF_FFFF; @@ -12,16 +14,20 @@ } } +struct FracBits; #[derive(Clone, Debug, Copy)] -pub struct FPNum { +pub struct FixedPoint { sign_mask: u64, value: u64, + _marker: PhantomData>, } -impl FPNum { +pub type FPNum = FixedPoint<20>; + +impl FixedPoint { #[inline] pub fn new(numerator: i32, denominator: u32) -> Self { - FPNum::from(numerator) / denominator + Self::from(numerator) / denominator } #[inline] @@ -49,24 +55,26 @@ Self { sign_mask: POSITIVE_MASK, value: self.value, + _marker: self._marker, } } #[inline] - pub fn round(&self) -> i32 { - ((self.value >> 32) as i32 ^ self.sign_mask as i32).wrapping_sub(self.sign_mask as i32) + pub fn round(&self) -> i64 { + ((self.value >> FRAC_BITS) as i64 ^ self.sign_mask as i64).wrapping_sub(self.sign_mask as i64) } #[inline] - pub const fn abs_round(&self) -> u32 { - (self.value >> 32) as u32 + pub const fn abs_round(&self) -> u64 { + self.value >> FRAC_BITS } #[inline] pub fn sqr(&self) -> Self { Self { sign_mask: 0, - value: ((self.value as u128).pow(2) >> 32) as u64, + value: ((self.value as u128).pow(2) >> FRAC_BITS).saturating_into(), + _marker: self._marker } } @@ -76,92 +84,95 @@ Self { sign_mask: POSITIVE_MASK, - value: integral_sqrt(self.value) << 16, + value: integral_sqrt(self.value) << (FRAC_BITS / 2), + _marker: self._marker } } #[inline] - pub fn with_sign(&self, is_negative: bool) -> FPNum { - FPNum { + pub fn with_sign(&self, is_negative: bool) -> Self { + Self { sign_mask: bool_mask(is_negative), ..*self } } #[inline] - pub const fn with_sign_as(self, other: FPNum) -> FPNum { - FPNum { + pub const fn with_sign_as(self, other: Self) -> Self { + Self { sign_mask: other.sign_mask, ..self } } - +/* #[inline] pub const fn point(self) -> FPPoint { FPPoint::new(self, self) } - +*/ #[inline] const fn temp_i128(self) -> i128 { ((self.value ^ self.sign_mask) as i128).wrapping_sub(self.sign_mask as i128) } } -impl From for FPNum { +impl From for FixedPoint { #[inline] fn from(n: i32) -> Self { - FPNum { + Self { sign_mask: bool_mask(n < 0), - value: (n.abs() as u64) << 32, + value: (n.abs() as u64) << FRAC_BITS, + _marker: PhantomData, } } } -impl From for FPNum { +impl From for FixedPoint { #[inline] fn from(n: u32) -> Self { Self { sign_mask: POSITIVE_MASK, - value: (n as u64) << 32, + value: (n as u64) << FRAC_BITS, + _marker: PhantomData, } } } -impl From for f64 { +impl From> for f64 { #[inline] - fn from(n: FPNum) -> Self { + fn from(n: FixedPoint) -> Self { if n.is_negative() { - n.value as f64 / -0x1_0000_0000i64 as f64 + n.value as f64 / -(1i64 << FRAC_BITS) as f64 } else { - n.value as f64 / 0x1_0000_0000i64 as f64 + n.value as f64 / (1i64 << FRAC_BITS) as f64 } } } -impl PartialEq for FPNum { +impl PartialEq for FixedPoint { #[inline] fn eq(&self, other: &Self) -> bool { self.value == other.value && (self.sign_mask == other.sign_mask || self.value == 0) } } -impl Eq for FPNum {} +impl Eq for FixedPoint {} -impl PartialOrd for FPNum { +impl PartialOrd for FixedPoint { #[inline] - fn partial_cmp(&self, rhs: &Self) -> std::option::Option { + fn partial_cmp(&self, rhs: &Self) -> Option { Some(self.cmp(rhs)) } } -impl Ord for FPNum { +impl Ord for FixedPoint { #[inline] fn cmp(&self, rhs: &Self) -> cmp::Ordering { self.temp_i128().cmp(&(rhs.temp_i128())) } } -impl ops::Add for FPNum { +impl ops::Add for FixedPoint { type Output = Self; #[inline] @@ -171,11 +182,12 @@ Self { sign_mask: mask, value: ((tmp as u64) ^ mask).wrapping_sub(mask), + _marker: PhantomData, } } } -impl ops::Sub for FPNum { +impl ops::Sub for FixedPoint { type Output = Self; #[inline] @@ -185,7 +197,7 @@ } } -impl ops::Neg for FPNum { +impl ops::Neg for FixedPoint { type Output = Self; #[inline] @@ -193,47 +205,51 @@ Self { sign_mask: !self.sign_mask, value: self.value, + _marker: PhantomData, } } } -impl ops::Mul for FPNum { +impl ops::Mul for FixedPoint { type Output = Self; #[inline] fn mul(self, rhs: Self) -> Self { Self { sign_mask: self.sign_mask ^ rhs.sign_mask, - value: ((self.value as u128 * rhs.value as u128) >> 32) as u64, + value: ((self.value as u128 * rhs.value as u128) >> FRAC_BITS).saturating_into(), + _marker: PhantomData, } } } -impl ops::Mul for FPNum { +impl ops::Mul for FixedPoint { type Output = Self; #[inline] fn mul(self, rhs: i32) -> Self { Self { sign_mask: self.sign_mask ^ bool_mask(rhs < 0), - value: self.value * rhs.abs() as u64, + value: (self.value as u128 * rhs.abs() as u128).saturating_into(), + _marker: PhantomData, } } } -impl ops::Div for FPNum { +impl ops::Div for FixedPoint { type Output = Self; #[inline] fn div(self, rhs: Self) -> Self { Self { sign_mask: self.sign_mask ^ rhs.sign_mask, - value: (((self.value as u128) << 32) / rhs.value as u128) as u64, + value: (((self.value as u128) << FRAC_BITS) / rhs.value as u128).saturating_into(), + _marker: PhantomData, } } } -impl ops::Div for FPNum { +impl ops::Div for FixedPoint { type Output = Self; #[inline] @@ -241,11 +257,12 @@ Self { sign_mask: self.sign_mask ^ bool_mask(rhs < 0), value: self.value / rhs.abs() as u64, + _marker: PhantomData, } } } -impl ops::Div for FPNum { +impl ops::Div for FixedPoint { type Output = Self; #[inline] @@ -253,6 +270,7 @@ Self { sign_mask: self.sign_mask, value: self.value / rhs as u64, + _marker: PhantomData, } } } @@ -308,6 +326,7 @@ FPNum { sign_mask: self.x_sign_mask as i32 as u64, value: self.x_value, + _marker: PhantomData, } } @@ -316,6 +335,7 @@ FPNum { sign_mask: self.y_sign_mask as i32 as u64, value: self.y_value, + _marker: PhantomData, } } @@ -326,7 +346,7 @@ #[inline] pub fn max_norm(&self) -> FPNum { - std::cmp::max(self.x().abs(), self.y().abs()) + cmp::max(self.x().abs(), self.y().abs()) } #[inline] @@ -345,6 +365,7 @@ FPNum { sign_mask: POSITIVE_MASK, value: integral_sqrt_ext(sqr), + _marker: PhantomData, } } } @@ -503,15 +524,17 @@ } #[inline] -pub fn distance(x: T, y: T) -> FPNum +pub fn distance(x: T, y: T) -> FixedPoint where - T: Into + std::fmt::Debug, + T: Into + std::fmt::Debug, { - let sqr: u128 = (x.into().pow(2) as u128).shl(64) + (y.into().pow(2) as u128).shl(64); + let [x_squared, y_squared] = [x, y].map(|i| (i.into().pow(2) as u128).saturating_mul(1 << FRAC_BITS << FRAC_BITS)); + let sqr: u128 = x_squared.saturating_add(y_squared); - FPNum { + FixedPoint { sign_mask: POSITIVE_MASK, value: integral_sqrt_ext(sqr), + _marker: PhantomData, } } diff -r 2b4f361e3891 -r 629d5123a979 rust/integral-geometry/src/lib.rs --- a/rust/integral-geometry/src/lib.rs Sat Sep 28 22:27:13 2024 +0200 +++ b/rust/integral-geometry/src/lib.rs Tue Dec 31 15:19:43 2024 +0100 @@ -4,7 +4,7 @@ ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, RangeInclusive, Sub, SubAssign}, }; -#[derive(PartialEq, Eq, Clone, Copy, Debug)] +#[derive(PartialEq, Eq, Clone, Copy, Debug, Hash)] pub struct Point { pub x: i32, pub y: i32, @@ -61,6 +61,14 @@ pub const fn rotate90(self) -> Self { Self::new(self.y, -self.x) } + #[inline] + pub const fn rotate180(self) -> Self { + Self::new(-self.x, -self.y) + } + #[inline] + pub const fn rotate270(self) -> Self { + Self::new(-self.y, self.x) + } #[inline] pub const fn cross(self, other: Point) -> i32 { @@ -99,7 +107,7 @@ #[inline] pub fn from_fppoint(p: &FPPoint) -> Self { - Self::new(p.x().round(), p.y().round()) + Self::new(p.x().round() as i32, p.y().round() as i32) } } @@ -164,6 +172,11 @@ } #[inline] + pub fn is_square(&self) -> bool { + self.width == self.height + } + + #[inline] pub const fn contains(&self, other: Self) -> bool { self.width >= other.width && self.height >= other.height } @@ -491,7 +504,7 @@ #[inline] pub fn with_margin(&self, margin: i32) -> Self { - let offset = Point::diag(margin); + let offset = Point::new(min(margin, self.width() as i32 / 2), min(margin, self.height() as i32 / 2)); Self::new(self.top_left() + offset, self.bottom_right() - offset) } @@ -601,11 +614,11 @@ self.vertices.insert(edge_index + 1, vertex); } - pub fn iter<'a>(&'a self) -> impl Iterator + 'a { + pub fn iter(&self) -> impl Iterator { (&self.vertices[..self.edges_count()]).iter() } - pub fn iter_mut<'a>(&'a mut self) -> impl Iterator + 'a { + pub fn iter_mut(&mut self) -> impl Iterator { let edges_count = self.edges_count(); let start = self.vertices.as_mut_ptr(); let end = unsafe { start.add(edges_count) }; diff -r 2b4f361e3891 -r 629d5123a979 rust/land2d/Cargo.toml --- a/rust/land2d/Cargo.toml Sat Sep 28 22:27:13 2024 +0200 +++ b/rust/land2d/Cargo.toml Tue Dec 31 15:19:43 2024 +0100 @@ -2,7 +2,7 @@ name = "land2d" version = "0.1.0" authors = ["Andrey Korotaev "] -edition = "2018" +edition = "2021" [dependencies] vec2d = { path = "../vec2d" } diff -r 2b4f361e3891 -r 629d5123a979 rust/land2d/src/lib.rs --- a/rust/land2d/src/lib.rs Sat Sep 28 22:27:13 2024 +0200 +++ b/rust/land2d/src/lib.rs Tue Dec 31 15:19:43 2024 +0100 @@ -1,24 +1,25 @@ -use std::{cmp, ops::Index}; - +use std::{cmp, ops::Index, ops::IndexMut}; +use vec2d::Vec2D; use integral_geometry::{ArcPoints, EquidistantPoints, Line, Point, PotSize, Rect, Size, SizeMask}; +#[derive(Debug)] pub struct Land2D { pixels: vec2d::Vec2D, play_box: Rect, mask: SizeMask, } -impl Land2D { - pub fn new(play_size: Size, fill_value: T) -> Self { +impl Land2D { + pub fn new(play_size: &Size, fill_value: T) -> Self { let real_size = play_size.next_power_of_two(); let top_left = Point::new( ((real_size.width() - play_size.width) / 2) as i32, (real_size.height() - play_size.height) as i32, ); - let play_box = Rect::from_size(top_left, play_size); + let play_box = Rect::from_size(top_left, *play_size); Self { play_box, - pixels: vec2d::Vec2D::new(real_size.size(), fill_value), + pixels: vec2d::Vec2D::new(&real_size.size(), fill_value), mask: real_size.to_mask(), } } @@ -99,6 +100,18 @@ } #[inline] + pub fn get(&self, y: i32, x: i32) -> T { + if self.is_valid_coordinate(x, y) { + unsafe { + // hey, I just checked that coordinates are valid! + *self.pixels.get_unchecked(y as usize, x as usize) + } + } else { + T::default() + } + } + + #[inline] pub fn map_point U>(&mut self, point: Point, f: F) -> U { self.map(point.y, point.x, f) } @@ -288,6 +301,30 @@ } } +impl IndexMut for Land2D { + #[inline] + fn index_mut(&mut self, row: usize) -> &mut [T] { + &mut self.pixels[row] + } +} + +impl From> for Land2D { + fn from(vec: Vec2D) -> Self { + let actual_size = vec.size(); + let pot_size = actual_size.next_power_of_two(); + + assert_eq!(actual_size, pot_size.size()); + + let top_left = Point::new(0, 0); + let play_box = Rect::from_size(top_left, actual_size); + Self { + play_box, + pixels: vec, + mask: pot_size.to_mask(), + } + } +} + #[cfg(test)] mod tests { use super::*; diff -r 2b4f361e3891 -r 629d5123a979 rust/landgen/Cargo.toml --- a/rust/landgen/Cargo.toml Sat Sep 28 22:27:13 2024 +0200 +++ b/rust/landgen/Cargo.toml Tue Dec 31 15:19:43 2024 +0100 @@ -2,9 +2,18 @@ name = "landgen" version = "0.1.0" authors = ["Andrey Korotaev "] -edition = "2018" +edition = "2021" [dependencies] integral-geometry = { path = "../integral-geometry" } land2d = { path = "../land2d" } -itertools = "0.7.8" +vec2d = { path = "../vec2d" } +itertools = "0.13" +png = "0.17" + +[dev-dependencies] +criterion = "0.5" + +[[bench]] +name = "benchmark" +harness = false diff -r 2b4f361e3891 -r 629d5123a979 rust/landgen/benches/benchmark.rs --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/rust/landgen/benches/benchmark.rs Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,58 @@ +use criterion::{black_box, criterion_group, criterion_main, Criterion}; +use integral_geometry::{Point, Rect, Size}; +use landgen; +use landgen::{LandGenerationParameters, LandGenerator}; +use landgen::outline_template_based::outline_template::OutlineTemplate; +use landgen::outline_template_based::template_based::TemplatedLandGenerator; + +pub fn generate_outline(c: &mut Criterion) { + let template = OutlineTemplate { + islands: vec![ + vec![ + Rect::from_box(273, 273, 2048, 2048), + Rect::from_box(683, 683, 32, 63), + Rect::from_box(1092, 1092, 2048, 2048), + ], + vec![ + Rect::from_box(1638, 1638, 2048, 2048), + Rect::from_box(2048, 2048, 32, 63), + Rect::from_box(2458, 2458, 2048, 2048), + ], + vec![ + Rect::from_box(3004, 3004, 2048, 2048), + Rect::from_box(3413, 3413, 32, 63), + Rect::from_box(3823, 3823, 2048, 2048), + ], + ], + walls: vec![], + fill_points: vec![Point::new(1, 0)], + size: Size { + width: 4096, + height: 2048, + }, + can_flip: false, + can_invert: false, + can_mirror: false, + is_negative: false, +}; + + let parameters = LandGenerationParameters::new( + 0u16, + 32768u16, + 10, + false, + false + ); + + c.bench_function("outline_generation", |b| b.iter(|| { + fn prng() -> impl Iterator { + (0..).map(|i| (i as u64 * 31_234_773 % 2_017_234_567) as u32) + } + + let gen = TemplatedLandGenerator::new(black_box(template.clone())); + gen.generate_land(black_box(¶meters), &mut prng()) + })); +} + +criterion_group!(benches, generate_outline); +criterion_main!(benches); diff -r 2b4f361e3891 -r 629d5123a979 rust/landgen/src/lib.rs --- a/rust/landgen/src/lib.rs Sat Sep 28 22:27:13 2024 +0200 +++ b/rust/landgen/src/lib.rs Tue Dec 31 15:19:43 2024 +0100 @@ -1,7 +1,8 @@ -mod outline; -pub mod outline_template; -pub mod template_based; +pub mod maze; +pub mod outline_template_based; +pub mod wavefront_collapse; +#[derive(Clone, Copy)] pub struct LandGenerationParameters { zero: T, basic: T, @@ -10,7 +11,7 @@ skip_bezier: bool, } -impl LandGenerationParameters { +impl LandGenerationParameters { pub fn new( zero: T, basic: T, @@ -26,20 +27,20 @@ skip_bezier, } } + + pub fn zero(&self) -> T { + self.zero + } + + pub fn basic(&self) -> T { + self.basic + } } pub trait LandGenerator { - fn generate_land>( + fn generate_land>( &self, parameters: &LandGenerationParameters, random_numbers: &mut I, ) -> land2d::Land2D; } - -#[cfg(test)] -mod tests { - #[test] - fn it_works() { - assert_eq!(2 + 2, 4); - } -} diff -r 2b4f361e3891 -r 629d5123a979 rust/landgen/src/maze.rs --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/rust/landgen/src/maze.rs Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,477 @@ +use crate::outline_template_based::outline::OutlinePoints; +use crate::{LandGenerationParameters, LandGenerator}; +use integral_geometry::{Point, Polygon, Rect, Size}; +use land2d::Land2D; + +#[derive(Clone)] +pub struct MazeTemplate { + pub width: usize, + pub height: usize, + pub cell_size: usize, + pub inverted: bool, + pub distortion_limiting_factor: u32, + pub braidness: u32, +} + +struct Maze { + inverted: bool, + braidness: u32, + off: Point, + num_cells: Size, + num_edges: Size, + seen_cells: Size, + cell_size: usize, + seen_list: Vec>>, + walls: Vec>>, + edge_list: Vec>>, + last_cell: Vec, + came_from: Vec>, + came_from_pos: Vec, +} + +fn in_line(p1: &Point, p2: &Point, p3: &Point) -> bool { + p1.x == p2.x && p2.x == p3.x || p1.y == p2.y && p2.y == p3.y +} + +#[derive(Clone, Copy)] +struct Direction(Point); + +impl Direction { + #[inline] + pub fn new(direction: usize) -> Self { + match direction % 4 { + 0 => Self(Point::new(0, -1)), + 1 => Self(Point::new(1, 0)), + 2 => Self(Point::new(0, 1)), + 3 => Self(Point::new(-1, 0)), + _ => panic!("Impossible"), + } + } + + #[inline] + pub fn rotate_right(self) -> Self { + Self(self.0.rotate90()) + } + + #[inline] + pub fn rotate_left(self) -> Self { + Self(self.0.rotate270()) + } + + #[inline] + pub fn to_edge(self) -> Self { + Self(Point::new( + if self.0.x < 0 { 0 } else { self.0.x }, + if self.0.y < 0 { 0 } else { self.0.y }, + )) + } + + #[inline] + pub fn orientation(&self) -> usize { + if self.0.x == 0 { + 0 + } else { + 1 + } + } +} + +impl Maze { + pub fn new>( + size: &Size, + cell_size: usize, + num_steps: usize, + inverted: bool, + braidness: u32, + random_numbers: &mut I, + ) -> Self { + let num_cells = Size::new( + prev_odd(size.width / cell_size), + prev_odd(size.height / cell_size), + ); + + let num_edges = Size::new(num_cells.width - 1, num_cells.height - 1); + let seen_cells = Size::new(num_cells.width / 2, num_cells.height / 2); + + let mut last_cell = vec![Point::diag(0); num_steps]; + let came_from_pos = vec![0; num_steps]; + let came_from = vec![vec![Point::diag(0); num_steps]; num_cells.area()]; + + let seen_list = vec![vec![None as Option; seen_cells.width]; seen_cells.height]; + let walls = vec![vec![vec![true; seen_cells.width]; seen_cells.height]; 2]; + let edge_list = vec![vec![vec![false; num_cells.width]; num_cells.height]; 2]; + + for current_step in 0..num_steps { + let x = random_numbers.next().unwrap_or_default() as usize % (seen_cells.width - 1) + / num_steps; + last_cell[current_step] = Point::new( + (x + current_step * seen_cells.width / num_steps) as i32, + random_numbers.next().unwrap_or_default() as i32 % seen_cells.height as i32, + ); + } + + let off_x = ((size.width - num_cells.width * cell_size) / 2) as i32; + let off_y = ((size.height - num_cells.height * cell_size) / 2) as i32; + + Self { + inverted, + braidness, + off: Point::new(off_x, off_y), + num_cells, + num_edges, + seen_cells, + cell_size, + seen_list, + walls, + edge_list, + last_cell, + came_from, + came_from_pos, + } + } + + fn see_cell>( + &mut self, + current_step: usize, + start_dir: Direction, + random_numbers: &mut I, + ) -> bool { + let mut dir = start_dir; + loop { + let p = self.last_cell[current_step]; + self.seen_list[p.y as usize][p.x as usize] = Some(current_step); + + let next_dir_clockwise = random_numbers.next().unwrap_or_default() % 2 == 0; + + for _ in 0..5 { + let sp = p + dir.0; + let when_seen = if sp.x < 0 + || sp.x >= self.seen_cells.width as i32 + || sp.y < 0 + || sp.y >= self.seen_cells.height as i32 + { + Some(current_step) + } else { + self.seen_list[sp.y as usize][sp.x as usize] + }; + + match when_seen { + Some(a) if a == current_step => { + // try another direction + if !self.inverted + && random_numbers.next().unwrap_or_default() % self.braidness == 0 + { + if dir.0.x == -1 && p.x > 0 { + self.walls[dir.orientation()][p.y as usize][p.x as usize - 1] = + false; + } + if dir.0.x == 1 && p.x < self.seen_cells.width as i32 - 1 { + self.walls[dir.orientation()][p.y as usize][p.x as usize] = false; + } + if dir.0.y == -1 && p.y > 0 { + self.walls[dir.orientation()][p.y as usize - 1][p.x as usize] = + false; + } + if dir.0.y == 1 && p.y < self.seen_cells.height as i32 - 1 { + self.walls[dir.orientation()][p.y as usize][p.x as usize] = false; + } + } + + if next_dir_clockwise { + dir = dir.rotate_right(); + } else { + dir = dir.rotate_left(); + } + } + None => { + // cell was not seen yet, go there + let o_dir = dir.rotate_right().rotate_right(); + let op = p - o_dir.to_edge().0; + self.walls[o_dir.orientation()][op.y as usize][op.x as usize] = false; + self.last_cell[current_step] = sp; + self.came_from_pos[current_step] += 1; + self.came_from[self.came_from_pos[current_step] as usize][current_step] = p; + return false; + } + _ => { + return true; + } + } + } + + self.last_cell[current_step] = + self.came_from[self.came_from_pos[current_step] as usize][current_step]; + self.came_from_pos[current_step] -= 1; + + if self.came_from_pos[current_step] < 0 { + return true; + } + } + } + + fn add_vertex(&mut self, p: Point, polygon: &mut Vec) { + let [x, y] = [p.x, p.y].map(|i| { + if self.inverted || i & 1 == 0 { + self.cell_size + } else { + self.cell_size * 2 / 3 + } + }); + let new_point = Point::new( + (p.x - 1) * self.cell_size as i32 + x as i32 + self.off.x, + (p.y - 1) * self.cell_size as i32 + y as i32 + self.off.y, + ); + + let nv = polygon.len(); + if nv > 2 { + if in_line(&polygon[nv - 2], &polygon[nv - 1], &new_point) { + polygon.pop(); + } + } + + polygon.push(new_point); + } + + fn add_edge(&mut self, p: Point, mut dir: Direction, polygon: &mut Vec) { + let mut next_p = Some(p); + + while let Some(p) = next_p { + next_p = None; + + for _ in 0..4 { + let cdir = dir.to_edge(); + + let np = p + cdir.0; + + if np.x >= 0 + && np.y >= 0 + && np.x < self.num_cells.width as i32 + && np.y < self.num_cells.height as i32 + && self.edge_list[dir.orientation()][np.y as usize][np.x as usize] + { + self.edge_list[dir.orientation()][np.y as usize][np.x as usize] = false; + self.add_vertex(p + dir.0 + Point::new(1, 1), polygon); + next_p = Some(p + dir.0); + break; + } + + dir = dir.rotate_right(); + } + } + } + + pub fn to_islands(mut self) -> (Vec, Vec) { + let mut islands: Vec = vec![]; + let mut polygon: Vec = vec![]; + let mut maze = vec![vec![false; self.num_cells.width]; self.num_cells.height]; + + for x in 0..self.seen_cells.width { + for y in 0..self.seen_cells.height { + if self.seen_list[y][x].is_some() { + maze[y * 2 + 1][x * 2 + 1] = true; + } + } + + for y in 0..self.seen_cells.height - 1 { + if !self.walls[0][y][x] { + maze[y * 2 + 2][x * 2 + 1] = true; + } + } + } + + for x in 0..self.seen_cells.width - 1 { + for y in 0..self.seen_cells.height { + if !self.walls[1][y][x] { + maze[y * 2 + 1][x * 2 + 2] = true; + } + } + } + + for x in 0..self.num_edges.width { + for y in 0..self.num_cells.height { + self.edge_list[0][y][x] = maze[y][x] != maze[y][x + 1]; + } + } + + for x in 0..self.num_cells.width { + for y in 0..self.num_edges.height { + self.edge_list[1][y][x] = maze[y][x] != maze[y + 1][x]; + } + } + + let mut fill_points = vec![]; + + for x in 0..self.num_edges.width { + for y in 0..self.num_cells.height { + if self.edge_list[0][y][x] { + self.edge_list[0][y][x] = false; + self.add_vertex(Point::new(x as i32 + 1, y as i32 + 1), &mut polygon); + self.add_vertex(Point::new(x as i32 + 1, y as i32), &mut polygon); + self.add_edge( + Point::new(x as i32, y as i32 - 1), + Direction::new(0), + &mut polygon, + ); + + if polygon.len() > 4 { + if in_line(polygon.last().unwrap(), &polygon[0], &polygon[1]) { + polygon.pop(); + } + + for p in &polygon { + println!("{} {}", p.x, p.y); + } + println!("\ne\n"); + + islands.push(Polygon::new(&polygon)); + } + polygon.clear(); + } + } + } + + for x in 0..self.num_cells.width { + for y in 0..self.num_cells.height { + if maze[y][x] { + let half_cell = self.cell_size / 2; + let fill_point = Point::new( + (x * self.cell_size + half_cell) as i32 + self.off.x, + (y * self.cell_size + half_cell) as i32 + self.off.y, + ); + islands.push(Polygon::new(&[fill_point])); + fill_points.push(fill_point); + + let mut points = vec![(x, y)]; + + while let Some((x, y)) = points.pop() { + if maze[y][x] { + maze[y][x] = false; + + if x > 0 { + points.push((x - 1, y)); + } + if x < self.num_cells.width - 1 { + points.push((x + 1, y)); + } + if y > 0 { + points.push((x, y - 1)); + } + if y < self.num_cells.height - 1 { + points.push((x, y + 1)); + } + } + } + } + } + } + + (islands, fill_points) + } +} + +pub struct MazeLandGenerator { + maze_template: MazeTemplate, +} + +fn prev_odd(num: usize) -> usize { + if num & 1 == 0 { + num - 1 + } else { + num + } +} + +impl MazeLandGenerator { + pub fn new(maze_template: MazeTemplate) -> Self { + Self { maze_template } + } + + fn generate_outline>( + &self, + size: &Size, + play_box: Rect, + intersections_box: Rect, + random_numbers: &mut I, + ) -> OutlinePoints { + let num_steps = if self.maze_template.inverted { 3 } else { 1 }; + let mut step_done = vec![false; num_steps]; + let mut done = false; + + let mut maze = Maze::new( + &size, + self.maze_template.cell_size, + num_steps, + self.maze_template.inverted, + self.maze_template.braidness, + random_numbers, + ); + + while !done { + done = true; + + for current_step in 0..num_steps { + if !step_done[current_step] { + let dir = Direction::new(random_numbers.next().unwrap_or_default() as usize); + step_done[current_step] = maze.see_cell(current_step, dir, random_numbers); + done = false; + } + } + } + + let (islands, fill_points) = maze.to_islands(); + + OutlinePoints { + islands, + walls: vec![], + fill_points, + size: *size, + play_box, + intersections_box, + } + } +} + +impl LandGenerator for MazeLandGenerator { + fn generate_land>( + &self, + parameters: &LandGenerationParameters, + random_numbers: &mut I, + ) -> Land2D { + let do_invert = self.maze_template.inverted; + let (basic, zero) = if do_invert { + (parameters.zero, parameters.basic) + } else { + (parameters.basic, parameters.zero) + }; + + let land_size = Size::new(self.maze_template.width, self.maze_template.height); + let mut land = Land2D::new(&land_size, basic); + + let mut points = self.generate_outline( + &land.size().size(), + land.play_box(), //??? Rect::at_origin(land_size).with_margin(land_size.to_square().width as i32 * -2), + land.play_box(), + random_numbers, + ); + + if !parameters.skip_distort { + points.distort( + parameters.distance_divisor, + self.maze_template.distortion_limiting_factor, + random_numbers, + ); + } + + if !parameters.skip_bezier { + points.bezierize(5); + } + + points.draw(&mut land, zero); + + for p in &points.fill_points { + land.fill(*p, zero, zero) + } + + land + } +} diff -r 2b4f361e3891 -r 629d5123a979 rust/landgen/src/outline.rs --- a/rust/landgen/src/outline.rs Sat Sep 28 22:27:13 2024 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,341 +0,0 @@ -use itertools::Itertools; -use std::cmp::min; - -use integral_geometry::{Line, Point, Polygon, Ray, Rect, Size}; -use land2d::Land2D; - -use crate::outline_template::OutlineTemplate; - -pub struct OutlinePoints { - pub islands: Vec, - pub fill_points: Vec, - pub size: Size, - pub play_box: Rect, - intersections_box: Rect, -} - -impl OutlinePoints { - pub fn from_outline_template>( - outline_template: &OutlineTemplate, - play_box: Rect, - size: Size, - random_numbers: &mut I, - ) -> Self { - Self { - play_box, - size, - islands: outline_template - .islands - .iter() - .map(|i| { - i.iter() - .zip(random_numbers.tuples()) - .map(|(rect, (rnd_a, rnd_b))| { - play_box.top_left() + rect.quotient(rnd_a as usize, rnd_b as usize) - }) - .collect::>() - .into() - }) - .collect(), - fill_points: outline_template.fill_points.clone(), - intersections_box: Rect::at_origin(size) - .with_margin(size.to_square().width as i32 * -2), - } - } - - pub fn total_len(&self) -> usize { - self.islands.iter().map(|i| i.edges_count()).sum::() + self.fill_points.len() - } - - pub fn iter(&self) -> impl Iterator { - self.islands - .iter() - .flat_map(|p| p.iter()) - .chain(self.fill_points.iter()) - } - - pub fn iter_mut(&mut self) -> impl Iterator { - self.islands - .iter_mut() - .flat_map(|i| i.iter_mut()) - .chain(self.fill_points.iter_mut()) - } - - fn divide_edge>( - &self, - segment: Line, - distance_divisor: u32, - random_numbers: &mut I, - ) -> Option { - #[inline] - fn intersects(ray: &Ray, edge: &Line) -> bool { - ray.orientation(edge.start) != ray.orientation(edge.end) - } - - #[inline] - fn solve_intersection( - intersections_box: &Rect, - ray: &Ray, - edge: &Line, - ) -> Option<(i32, u32)> { - let edge_dir = edge.scaled_direction(); - let aqpb = ray.direction.cross(edge_dir) as i64; - - if aqpb != 0 { - let mut iy = ((((edge.start.x - ray.start.x) as i64 * ray.direction.y as i64 - + ray.start.y as i64 * ray.direction.x as i64) - * edge_dir.y as i64 - - edge.start.y as i64 * edge_dir.x as i64 * ray.direction.y as i64) - / aqpb) as i32; - - // is there better way to do it? - if iy < intersections_box.top() { - iy = intersections_box.top(); - } else if iy > intersections_box.bottom() { - iy = intersections_box.bottom(); - } - - let ix = if ray.direction.y.abs() > edge_dir.y.abs() { - ray.start.x + ray.direction.cotangent_mul(iy - ray.start.y) - } else { - edge.start.x + edge_dir.cotangent_mul(iy - edge.start.y) - }; - - let intersection_point = Point::new(ix, iy).clamp(intersections_box); - let diff_point = ray.start - intersection_point; - let t = ray.direction.dot(diff_point); - - if diff_point.max_norm() >= std::i16::MAX as i32 { - Some((t, std::i32::MAX as u32)) - } else { - let d = diff_point.integral_norm(); - - Some((t, d)) - } - } else { - None - } - } - - let min_distance = 40; - // new point should fall inside this box - let map_box = self.play_box.with_margin(min_distance); - - let normal = segment.scaled_normal(); - let normal_len = normal.integral_norm(); - let mid_point = segment.center(); - - if (normal_len < min_distance as u32 * 3) || !map_box.contains_inside(mid_point) { - return None; - } - - let normal_ray = Ray::new(mid_point, normal); - let mut dist_left = (self.size.width + self.size.height) as u32; - let mut dist_right = dist_left; - - // find distances to map borders - if normal.x != 0 { - // where the normal line intersects the left map border - let left_intersection = Point::new( - map_box.left(), - mid_point.y + normal.tangent_mul(map_box.left() - mid_point.x), - ); - dist_left = (mid_point - left_intersection).integral_norm(); - - // same for the right border - let right_intersection = Point::new( - map_box.right(), - mid_point.y + normal.tangent_mul(map_box.right() - mid_point.x), - ); - dist_right = (mid_point - right_intersection).integral_norm(); - - if normal.x > 0 { - std::mem::swap(&mut dist_left, &mut dist_right); - } - } - - if normal.y != 0 { - // where the normal line intersects the top map border - let top_intersection = Point::new( - mid_point.x + normal.cotangent_mul(map_box.top() - mid_point.y), - map_box.top(), - ); - let dl = (mid_point - top_intersection).integral_norm(); - - // same for the bottom border - let bottom_intersection = Point::new( - mid_point.x + normal.cotangent_mul(map_box.bottom() - mid_point.y), - map_box.bottom(), - ); - let dr = (mid_point - bottom_intersection).integral_norm(); - - if normal.y < 0 { - dist_left = min(dist_left, dl); - dist_right = min(dist_right, dr); - } else { - dist_left = min(dist_left, dr); - dist_right = min(dist_right, dl); - } - } - - // now go through all other segments - for s in self.segments_iter() { - if s != segment { - if intersects(&normal_ray, &s) { - if let Some((t, d)) = - solve_intersection(&self.intersections_box, &normal_ray, &s) - { - if t > 0 { - dist_right = min(dist_right, d); - } else { - dist_left = min(dist_left, d); - } - } - } - } - } - - // go through all points, including fill points - for pi in self.iter().cloned() { - if pi != segment.start && pi != segment.end { - if intersects(&pi.ray_with_dir(normal), &segment) { - // ray from segment.start - if let Some((t, d)) = solve_intersection( - &self.intersections_box, - &normal_ray, - &segment.start.line_to(pi), - ) { - if t > 0 { - dist_right = min(dist_right, d); - } else { - dist_left = min(dist_left, d); - } - } - - // ray from segment.end - if let Some((t, d)) = solve_intersection( - &self.intersections_box, - &normal_ray, - &segment.end.line_to(pi), - ) { - if t > 0 { - dist_right = min(dist_right, d); - } else { - dist_left = min(dist_left, d); - } - } - } - } - } - - let max_dist = normal_len * 100 / distance_divisor; - dist_left = min(dist_left, max_dist); - dist_right = min(dist_right, max_dist); - - if dist_right + dist_left < min_distance as u32 * 2 + 10 { - // limits are too narrow, just divide - Some(mid_point) - } else { - // select distance within [-dist_right; dist_left], keeping min_distance in mind - let d = -(dist_right as i32) - + min_distance - + random_numbers.next().unwrap() as i32 - % (dist_right as i32 + dist_left as i32 - min_distance * 2); - - Some(mid_point + normal * d / normal_len as i32) - } - } - - fn divide_edges>( - &mut self, - distance_divisor: u32, - random_numbers: &mut I, - ) { - for is in 0..self.islands.len() { - let mut i = 0; - while i < self.islands[is].edges_count() { - let segment = self.islands[is].get_edge(i); - if let Some(new_point) = self.divide_edge(segment, distance_divisor, random_numbers) - { - self.islands[is].split_edge(i, new_point); - i += 2; - } else { - i += 1; - } - } - } - } - - pub fn bezierize(&mut self, segments_number: u32) { - for island in &mut self.islands { - island.bezierize(segments_number); - } - } - - pub fn distort>( - &mut self, - distance_divisor: u32, - random_numbers: &mut I, - ) { - loop { - let old_len = self.total_len(); - self.divide_edges(distance_divisor, random_numbers); - - if self.total_len() == old_len { - break; - } - } - } - - pub fn draw(&self, land: &mut Land2D, value: T) { - for segment in self.segments_iter() { - land.draw_line(segment, value); - } - } - - fn segments_iter<'a>(&'a self) -> impl Iterator + 'a { - self.islands.iter().flat_map(|p| p.iter_edges()) - } - - pub fn mirror(&mut self) { - let r = self.size.width as i32 - 1; - - self.iter_mut().for_each(|p| p.x = r - p.x); - } - - pub fn flip(&mut self) { - let t = self.size.height as i32 - 1; - - self.iter_mut().for_each(|p| p.y = t - p.y); - } -} - -#[test] -fn points_test() { - let size = Size::square(100); - let mut points = OutlinePoints { - islands: vec![ - Polygon::new(&[Point::new(0, 0), Point::new(20, 0), Point::new(30, 30)]), - Polygon::new(&[Point::new(10, 15), Point::new(15, 20), Point::new(20, 15)]), - ], - fill_points: vec![Point::new(1, 1)], - play_box: Rect::at_origin(size).with_margin(10), - size: Size::square(100), - intersections_box: Rect::at_origin(size), - }; - - let segments: Vec = points.segments_iter().collect(); - assert_eq!( - segments.first(), - Some(&Line::new(Point::new(0, 0), Point::new(20, 0))) - ); - assert_eq!( - segments.last(), - Some(&Line::new(Point::new(20, 15), Point::new(10, 15))) - ); - - points.iter_mut().for_each(|p| p.x = 2); - - assert_eq!(points.fill_points[0].x, 2); - assert_eq!(points.islands[0].get_edge(0).start.x, 2); -} diff -r 2b4f361e3891 -r 629d5123a979 rust/landgen/src/outline_template.rs --- a/rust/landgen/src/outline_template.rs Sat Sep 28 22:27:13 2024 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,75 +0,0 @@ -use integral_geometry::{Point, Rect, Size}; - -#[derive(Clone, Debug)] -pub struct OutlineTemplate { - pub islands: Vec>, - pub fill_points: Vec, - pub size: Size, - pub can_flip: bool, - pub can_invert: bool, - pub can_mirror: bool, - pub is_negative: bool, -} - -impl OutlineTemplate { - pub fn new(size: Size) -> Self { - OutlineTemplate { - size, - islands: Vec::new(), - fill_points: Vec::new(), - can_flip: false, - can_invert: false, - can_mirror: false, - is_negative: false, - } - } - - pub fn flippable(self) -> Self { - Self { - can_flip: true, - ..self - } - } - - pub fn mirrorable(self) -> Self { - Self { - can_mirror: true, - ..self - } - } - - pub fn invertable(self) -> Self { - Self { - can_invert: true, - ..self - } - } - - pub fn negative(self) -> Self { - Self { - is_negative: true, - ..self - } - } - - pub fn with_fill_points(self, fill_points: Vec) -> Self { - Self { - fill_points, - ..self - } - } - - pub fn with_islands(self, islands: Vec>) -> Self { - Self { islands, ..self } - } - - pub fn add_fill_points(mut self, points: &[Point]) -> Self { - self.fill_points.extend_from_slice(points); - self - } - - pub fn add_island(mut self, island: &[Rect]) -> Self { - self.islands.push(island.into()); - self - } -} diff -r 2b4f361e3891 -r 629d5123a979 rust/landgen/src/outline_template_based/mod.rs --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/rust/landgen/src/outline_template_based/mod.rs Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,3 @@ +pub mod outline; +pub mod outline_template; +pub mod template_based; diff -r 2b4f361e3891 -r 629d5123a979 rust/landgen/src/outline_template_based/outline.rs --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/rust/landgen/src/outline_template_based/outline.rs Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,371 @@ +use itertools::Itertools; +use std::cmp::min; + +use integral_geometry::{Line, Point, Polygon, Ray, Rect, Size}; +use land2d::Land2D; + +use super::outline_template::OutlineTemplate; + +pub struct OutlinePoints { + pub islands: Vec, + pub walls: Vec, + pub fill_points: Vec, + pub size: Size, + pub play_box: Rect, + pub intersections_box: Rect, +} + +impl OutlinePoints { + pub fn from_outline_template>( + outline_template: &OutlineTemplate, + play_box: Rect, + size: Size, + random_numbers: &mut I, + ) -> Self { + Self { + play_box, + size, + islands: outline_template + .islands + .iter() + .map(|i| { + i.iter() + .zip(random_numbers.tuples()) + .map(|(rect, (rnd_a, rnd_b))| { + play_box.top_left() + rect.quotient(rnd_a as usize, rnd_b as usize) + }) + .collect::>() + .into() + }) + .collect(), + walls: outline_template + .walls + .iter() + .map(|i| { + i.iter() + .zip(random_numbers.tuples()) + .map(|(rect, (rnd_a, rnd_b))| { + play_box.top_left() + rect.quotient(rnd_a as usize, rnd_b as usize) + }) + .collect::>() + .into() + }) + .collect(), + fill_points: outline_template.fill_points.clone(), + intersections_box: Rect::at_origin(size) + .with_margin(size.to_square().width as i32 * -2), + } + } + + pub fn total_len(&self) -> usize { + self.islands.iter().map(|i| i.edges_count()).sum::() + self.fill_points.len() + } + + pub fn iter(&self) -> impl Iterator { + self.islands + .iter() + .flat_map(|p| p.iter()) + .chain(self.walls.iter().flat_map(|p| p.iter())) + .chain(self.fill_points.iter()) + } + + pub fn iter_mut(&mut self) -> impl Iterator { + self.islands + .iter_mut() + .flat_map(|i| i.iter_mut()) + .chain(self.walls.iter_mut().flat_map(|p| p.iter_mut())) + .chain(self.fill_points.iter_mut()) + } + + fn divide_edge>( + &self, + segment: Line, + distance_divisor: u32, + distortion_limiting_factor: u32, + random_numbers: &mut I, + ) -> Option { + #[inline] + fn intersects(ray: &Ray, edge: &Line) -> bool { + ray.orientation(edge.start) != ray.orientation(edge.end) + } + + #[inline] + fn solve_intersection( + intersections_box: &Rect, + ray: &Ray, + edge: &Line, + ) -> Option<(i32, u32)> { + let edge_dir = edge.scaled_direction(); + let aqpb = ray.direction.cross(edge_dir) as i64; + + if aqpb != 0 { + let mut iy = ((((edge.start.x - ray.start.x) as i64 * ray.direction.y as i64 + + ray.start.y as i64 * ray.direction.x as i64) + * edge_dir.y as i64 + - edge.start.y as i64 * edge_dir.x as i64 * ray.direction.y as i64) + / aqpb) as i32; + + // is there better way to do it? + if iy < intersections_box.top() { + iy = intersections_box.top(); + } else if iy > intersections_box.bottom() { + iy = intersections_box.bottom(); + } + + let ix = if ray.direction.y.abs() > edge_dir.y.abs() { + ray.start.x + ray.direction.cotangent_mul(iy - ray.start.y) + } else { + edge.start.x + edge_dir.cotangent_mul(iy - edge.start.y) + }; + + let intersection_point = Point::new(ix, iy).clamp(intersections_box); + let diff_point = ray.start - intersection_point; + let t = ray.direction.dot(diff_point); + + if diff_point.max_norm() >= std::i16::MAX as i32 { + Some((t, std::i32::MAX as u32)) + } else { + let d = diff_point.integral_norm(); + + Some((t, d)) + } + } else { + None + } + } + + let min_distance = distance_divisor as i32; + // new point should fall inside this box + let map_box = self.play_box.with_margin(min_distance); + + let normal = segment.scaled_normal(); + let normal_len = normal.integral_norm(); + let mid_point = segment.center(); + + if (normal_len < min_distance as u32 * 3) || !map_box.contains_inside(mid_point) { + return None; + } + + let normal_ray = Ray::new(mid_point, normal); + let mut dist_left = (self.size.width + self.size.height) as u32; + let mut dist_right = dist_left; + + // find distances to map borders + if normal.x != 0 { + // where the normal line intersects the left map border + let left_intersection = Point::new( + map_box.left(), + mid_point.y + normal.tangent_mul(map_box.left() - mid_point.x), + ); + dist_left = (mid_point - left_intersection).integral_norm(); + + // same for the right border + let right_intersection = Point::new( + map_box.right(), + mid_point.y + normal.tangent_mul(map_box.right() - mid_point.x), + ); + dist_right = (mid_point - right_intersection).integral_norm(); + + if normal.x > 0 { + std::mem::swap(&mut dist_left, &mut dist_right); + } + } + + if normal.y != 0 { + // where the normal line intersects the top map border + let top_intersection = Point::new( + mid_point.x + normal.cotangent_mul(map_box.top() - mid_point.y), + map_box.top(), + ); + let dl = (mid_point - top_intersection).integral_norm(); + + // same for the bottom border + let bottom_intersection = Point::new( + mid_point.x + normal.cotangent_mul(map_box.bottom() - mid_point.y), + map_box.bottom(), + ); + let dr = (mid_point - bottom_intersection).integral_norm(); + + if normal.y < 0 { + dist_left = min(dist_left, dl); + dist_right = min(dist_right, dr); + } else { + dist_left = min(dist_left, dr); + dist_right = min(dist_right, dl); + } + } + + // now go through all other segments + for s in self.segments_iter() { + if s != segment && intersects(&normal_ray, &s) { + if let Some((t, d)) = solve_intersection(&self.intersections_box, &normal_ray, &s) { + if t > 0 { + dist_right = min(dist_right, d); + } else { + dist_left = min(dist_left, d); + } + } + } + } + + // go through all points, including fill points + for pi in self.iter().cloned() { + if pi != segment.start + && pi != segment.end + && intersects(&pi.ray_with_dir(normal), &segment) + { + // ray from segment.start + if let Some((t, d)) = solve_intersection( + &self.intersections_box, + &normal_ray, + &segment.start.line_to(pi), + ) { + if t > 0 { + dist_right = min(dist_right, d); + } else { + dist_left = min(dist_left, d); + } + } + + // ray from segment.end + if let Some((t, d)) = solve_intersection( + &self.intersections_box, + &normal_ray, + &segment.end.line_to(pi), + ) { + if t > 0 { + dist_right = min(dist_right, d); + } else { + dist_left = min(dist_left, d); + } + } + } + } + + let max_dist = normal_len * 128 / distortion_limiting_factor; + dist_left = min(dist_left, max_dist); + dist_right = min(dist_right, max_dist); + + if dist_right + dist_left < min_distance as u32 * 2 + 10 { + // limits are too narrow, just divide + Some(mid_point) + } else { + // select distance within [-dist_right; dist_left], keeping min_distance in mind + let d = -(dist_right as i32) + + min_distance + + random_numbers.next().unwrap() as i32 + % (dist_right as i32 + dist_left as i32 - min_distance * 2); + + Some(mid_point + normal * d / normal_len as i32) + } + } + + fn divide_edges>( + &mut self, + distance_divisor: u32, + distortion_limiting_factor: u32, + random_numbers: &mut I, + ) { + for is in 0..self.islands.len() { + let mut i = 0; + while i < self.islands[is].edges_count() { + let segment = self.islands[is].get_edge(i); + if let Some(new_point) = self.divide_edge( + segment, + distance_divisor, + distortion_limiting_factor, + random_numbers, + ) { + self.islands[is].split_edge(i, new_point); + i += 2; + } else { + i += 1; + } + } + } + } + + pub fn bezierize(&mut self, segments_number: u32) { + for island in &mut self.islands { + island.bezierize(segments_number); + } + } + + pub fn distort>( + &mut self, + distance_divisor: u32, + distortion_limiting_factor: u32, + random_numbers: &mut I, + ) { + loop { + let old_len = self.total_len(); + self.divide_edges(distance_divisor, distortion_limiting_factor, random_numbers); + + if self.total_len() == old_len { + break; + } + } + } + + pub fn draw(&self, land: &mut Land2D, value: T) { + for segment in self.visible_segments_iter() { + land.draw_line(segment, value); + } + } + + fn visible_segments_iter<'a>(&'a self) -> impl Iterator + 'a { + self.islands + .iter() + .flat_map(|p| p.iter_edges()) + } + + fn segments_iter<'a>(&'a self) -> impl Iterator + 'a { + self.islands + .iter() + .flat_map(|p| p.iter_edges()) + .chain(self.walls.iter().flat_map(|p| p.iter_edges())) + } + + pub fn mirror(&mut self) { + let r = self.size.width as i32 - 1; + + self.iter_mut().for_each(|p| p.x = r - p.x); + } + + pub fn flip(&mut self) { + let t = self.size.height as i32 - 1; + + self.iter_mut().for_each(|p| p.y = t - p.y); + } +} + +#[test] +fn points_test() { + let size = Size::square(100); + let mut points = OutlinePoints { + islands: vec![ + Polygon::new(&[Point::new(0, 0), Point::new(20, 0), Point::new(30, 30)]), + Polygon::new(&[Point::new(10, 15), Point::new(15, 20), Point::new(20, 15)]), + ], + walls: vec![], + fill_points: vec![Point::new(1, 1)], + play_box: Rect::at_origin(size).with_margin(10), + size: Size::square(100), + intersections_box: Rect::at_origin(size), + }; + + let segments: Vec = points.segments_iter().collect(); + assert_eq!( + segments.first(), + Some(&Line::new(Point::new(0, 0), Point::new(20, 0))) + ); + assert_eq!( + segments.last(), + Some(&Line::new(Point::new(20, 15), Point::new(10, 15))) + ); + + points.iter_mut().for_each(|p| p.x = 2); + + assert_eq!(points.fill_points[0].x, 2); + assert_eq!(points.islands[0].get_edge(0).start.x, 2); +} diff -r 2b4f361e3891 -r 629d5123a979 rust/landgen/src/outline_template_based/outline_template.rs --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/rust/landgen/src/outline_template_based/outline_template.rs Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,85 @@ +use integral_geometry::{Point, Rect, Size}; + +#[derive(Clone, Debug)] +pub struct OutlineTemplate { + pub islands: Vec>, + pub walls: Vec>, + pub fill_points: Vec, + pub size: Size, + pub can_flip: bool, + pub can_invert: bool, + pub can_mirror: bool, + pub is_negative: bool, +} + +impl OutlineTemplate { + pub fn new(size: Size) -> Self { + OutlineTemplate { + size, + islands: Vec::new(), + walls: Vec::new(), + fill_points: Vec::new(), + can_flip: false, + can_invert: false, + can_mirror: false, + is_negative: false, + } + } + + pub fn flippable(self) -> Self { + Self { + can_flip: true, + ..self + } + } + + pub fn mirrorable(self) -> Self { + Self { + can_mirror: true, + ..self + } + } + + pub fn invertable(self) -> Self { + Self { + can_invert: true, + ..self + } + } + + pub fn negative(self) -> Self { + Self { + is_negative: true, + ..self + } + } + + pub fn cavern(self) -> Self { + Self { + is_negative: true, + can_invert: false, + ..self + } + } + + pub fn with_fill_points(self, fill_points: Vec) -> Self { + Self { + fill_points, + ..self + } + } + + pub fn with_islands(self, islands: Vec>) -> Self { + Self { islands, ..self } + } + + pub fn add_fill_points(mut self, points: &[Point]) -> Self { + self.fill_points.extend_from_slice(points); + self + } + + pub fn add_island(mut self, island: &[Rect]) -> Self { + self.islands.push(island.into()); + self + } +} diff -r 2b4f361e3891 -r 629d5123a979 rust/landgen/src/outline_template_based/template_based.rs --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/rust/landgen/src/outline_template_based/template_based.rs Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,80 @@ +use super::{outline::OutlinePoints, outline_template::OutlineTemplate}; +use crate::{LandGenerationParameters, LandGenerator}; +use land2d::Land2D; + +pub struct TemplatedLandGenerator { + outline_template: OutlineTemplate, +} + +impl TemplatedLandGenerator { + pub fn new(outline_template: OutlineTemplate) -> Self { + Self { outline_template } + } +} + +impl LandGenerator for TemplatedLandGenerator { + fn generate_land>( + &self, + parameters: &LandGenerationParameters, + random_numbers: &mut I, + ) -> Land2D { + let do_invert = self.outline_template.is_negative + && (!self.outline_template.can_invert || random_numbers.next().unwrap() & 1 == 0); + let (basic, zero) = if do_invert { + (parameters.zero, parameters.basic) + } else { + (parameters.basic, parameters.zero) + }; + + let mut land = Land2D::new(&self.outline_template.size, basic); + + let mut points = OutlinePoints::from_outline_template( + &self.outline_template, + land.play_box(), + land.size().size(), + random_numbers, + ); + + // mirror + if self.outline_template.can_mirror { + if let Some(b) = random_numbers.next() { + if b & 1 != 0 { + points.mirror(); + } + } + } + + // flip + if self.outline_template.can_flip { + if let Some(b) = random_numbers.next() { + if b & 1 != 0 { + points.flip(); + } + } + } + + if !parameters.skip_distort { + let distortion_limiting_factor = 100 + random_numbers.next().unwrap() % 8 * 10; + + points.distort( + parameters.distance_divisor, + distortion_limiting_factor, + random_numbers, + ); + } + + if !parameters.skip_bezier { + points.bezierize(5); + } + + points.draw(&mut land, zero); + + for p in &points.fill_points { + land.fill(*p, zero, zero) + } + + points.draw(&mut land, basic); + + land + } +} diff -r 2b4f361e3891 -r 629d5123a979 rust/landgen/src/template_based.rs --- a/rust/landgen/src/template_based.rs Sat Sep 28 22:27:13 2024 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,69 +0,0 @@ -use crate::{ - outline::OutlinePoints, outline_template::OutlineTemplate, LandGenerationParameters, - LandGenerator, -}; -use integral_geometry::{Point, Size}; -use land2d::Land2D; - -pub struct TemplatedLandGenerator { - outline_template: OutlineTemplate, -} - -impl TemplatedLandGenerator { - pub fn new(outline_template: OutlineTemplate) -> Self { - Self { outline_template } - } -} - -impl LandGenerator for TemplatedLandGenerator { - fn generate_land>( - &self, - parameters: &LandGenerationParameters, - random_numbers: &mut I, - ) -> Land2D { - let mut land = Land2D::new(self.outline_template.size, parameters.basic); - - let mut points = OutlinePoints::from_outline_template( - &self.outline_template, - land.play_box(), - land.size().size(), - random_numbers, - ); - - // mirror - if self.outline_template.can_mirror { - if let Some(b) = random_numbers.next() { - if b & 1 != 0 { - points.mirror(); - } - } - } - - // flip - if self.outline_template.can_flip { - if let Some(b) = random_numbers.next() { - if b & 1 != 0 { - points.flip(); - } - } - } - - if !parameters.skip_distort { - points.distort(parameters.distance_divisor, random_numbers); - } - - if !parameters.skip_bezier { - points.bezierize(5); - } - - points.draw(&mut land, parameters.zero); - - for p in &points.fill_points { - land.fill(*p, parameters.zero, parameters.zero) - } - - points.draw(&mut land, parameters.basic); - - land - } -} diff -r 2b4f361e3891 -r 629d5123a979 rust/landgen/src/wavefront_collapse/generator.rs --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/rust/landgen/src/wavefront_collapse/generator.rs Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,344 @@ +use super::tile_image::{Edge, TileImage}; +use super::wavefront_collapse::{CollapseRule, Tile, WavefrontCollapse}; +use crate::{LandGenerationParameters, LandGenerator}; +use integral_geometry::Size; +use png::Decoder; +use std::collections::HashSet; +use std::fs::File; +use std::io::{BufReader, Result}; +use std::path::{Path, PathBuf}; + +#[derive(Clone)] +pub struct EdgeDescription { + pub name: String, + pub reversed: Option, + pub symmetrical: Option, +} + +#[derive(Clone)] +pub struct EdgesDescription { + pub top: EdgeDescription, + pub right: EdgeDescription, + pub bottom: EdgeDescription, + pub left: EdgeDescription, +} + +#[derive(Clone)] +pub struct TileDescription { + pub name: String, + pub edges: EdgesDescription, + pub is_negative: Option, + pub can_flip: Option, + pub can_mirror: Option, + pub can_rotate90: Option, + pub can_rotate180: Option, + pub can_rotate270: Option, +} + +#[derive(Clone)] +pub struct NonStrictEdgesDescription { + pub top: Option, + pub right: Option, + pub bottom: Option, + pub left: Option, +} + +#[derive(Clone)] +pub struct TemplateDescription { + pub size: Size, + pub tiles: Vec, + pub edges: NonStrictEdgesDescription, + pub wrap: bool, +} + +pub struct WavefrontCollapseLandGenerator { + pub template: TemplateDescription, + data_path: PathBuf, +} + +impl WavefrontCollapseLandGenerator { + pub fn new(template: TemplateDescription, data_path: &Path) -> Self { + Self { + template, + data_path: data_path.to_owned(), + } + } + + fn load_image_tiles( + &self, + parameters: &LandGenerationParameters, + tile_description: &TileDescription, + ) -> Result>> { + let mut result = Vec::new(); + + let file = File::open( + self.data_path + .join("Tiles") + .join(&tile_description.name) + .as_path(), + )?; + let decoder = Decoder::new(BufReader::new(file)); + let mut reader = decoder.read_info().unwrap(); + + let info = reader.info(); + let mut tiles_image = vec2d::Vec2D::new( + &Size::new(info.width as usize, info.height as usize), + parameters.zero, + ); + + let mut buf = vec![0; reader.output_buffer_size()]; + let info = reader.next_frame(&mut buf).unwrap(); + let bytes = &buf[..info.buffer_size()]; + + let mut tiles_image_pixels = tiles_image.as_mut_slice().iter_mut(); + + let (zero, basic) = if tile_description.is_negative.unwrap_or_default() { + (parameters.basic(), parameters.zero()) + } else { + (parameters.zero(), parameters.basic()) + }; + + match info.color_type.samples() { + 1 => { + for line in bytes.chunks_exact(info.line_size) { + for value in line.iter() { + *tiles_image_pixels + .next() + .expect("vec2d size matching image dimensions") = + if *value == 0 { zero } else { basic }; + } + } + } + a => { + for line in bytes.chunks_exact(info.line_size) { + for value in line.chunks_exact(a) { + print!("{:?},", value); + *tiles_image_pixels + .next() + .expect("vec2d size matching image dimensions") = + if value[0] == 0u8 { zero } else { basic }; + } + } + } + } + + let [top_edge, right_edge, bottom_edge, left_edge]: [Edge; 4] = [ + (&tile_description.edges.top).into(), + (&tile_description.edges.right).into(), + (&tile_description.edges.bottom).into(), + (&tile_description.edges.left).into(), + ]; + + let tile = + TileImage::::new(tiles_image, top_edge, right_edge, bottom_edge, left_edge); + + result.push(tile.clone()); + + if tile_description.can_flip.unwrap_or_default() { + result.push(tile.flipped()); + } + if tile_description.can_mirror.unwrap_or_default() { + result.push(tile.mirrored()); + } + if tile_description.can_flip.unwrap_or_default() + && tile_description.can_mirror.unwrap_or_default() + { + result.push(tile.mirrored().flipped()); + } + + if tile_description.can_rotate90.unwrap_or_default() { + result.push(tile.rotated90()); + } + if tile_description.can_rotate180.unwrap_or_default() { + result.push(tile.rotated180()); + } + if tile_description.can_rotate270.unwrap_or_default() { + result.push(tile.rotated270()); + } + + Ok(result) + } + + pub fn load_template( + &self, + parameters: &LandGenerationParameters, + ) -> Vec> { + let mut result = Vec::new(); + + for tile_description in self.template.tiles.iter() { + if let Ok(mut tiles) = self.load_image_tiles(parameters, tile_description) { + result.append(&mut tiles); + } else { + eprintln!("Failed to load a tile!"); + } + } + + result + } + + pub fn build_rules( + &self, + tiles: &[TileImage], + ) -> Vec { + let [grid_top_edge, grid_right_edge, grid_bottom_edge, grid_left_edge]: [Option< + Edge, + >; 4] = [ + self.template.edges.top.as_ref(), + self.template.edges.right.as_ref(), + self.template.edges.bottom.as_ref(), + self.template.edges.left.as_ref(), + ] + .map(|opt| opt.map(|d| d.into())); + + let mut rules = Vec::::new(); + + let default_connection = HashSet::from_iter(vec![Tile::Empty].into_iter()); + for (i, tile) in tiles.iter().enumerate() { + let mut right = default_connection.clone(); + let mut bottom = default_connection.clone(); + let mut left = default_connection.clone(); + let mut top = default_connection.clone(); + + // compatibility with grid edges + if grid_top_edge + .as_ref() + .map(|e| e.is_compatible(tile.top_edge())) + .unwrap_or(true) + { + top.insert(Tile::Outside); + } + if grid_right_edge + .as_ref() + .map(|e| e.is_compatible(tile.right_edge())) + .unwrap_or(true) + { + right.insert(Tile::Outside); + } + if grid_bottom_edge + .as_ref() + .map(|e| e.is_compatible(tile.bottom_edge())) + .unwrap_or(true) + { + bottom.insert(Tile::Outside); + } + if grid_left_edge + .as_ref() + .map(|e| e.is_compatible(tile.left_edge())) + .unwrap_or(true) + { + left.insert(Tile::Outside); + } + + // compatibility with itself + if tile.left_edge().is_compatible(tile.right_edge()) { + left.insert(Tile::Numbered(i)); + right.insert(Tile::Numbered(i)); + } + + if tile.top_edge().is_compatible(tile.bottom_edge()) { + top.insert(Tile::Numbered(i)); + bottom.insert(Tile::Numbered(i)); + } + + // compatibility with previously defined tiles + for p in 0..i { + if tiles[p].left_edge().is_compatible(tile.right_edge()) { + rules[p].left.insert(Tile::Numbered(i)); + right.insert(Tile::Numbered(p)); + } + + if tiles[p].right_edge().is_compatible(tile.left_edge()) { + rules[p].right.insert(Tile::Numbered(i)); + left.insert(Tile::Numbered(p)); + } + + if tiles[p].top_edge().is_compatible(tile.bottom_edge()) { + rules[p].top.insert(Tile::Numbered(i)); + bottom.insert(Tile::Numbered(p)); + } + + if tiles[p].bottom_edge().is_compatible(tile.top_edge()) { + rules[p].bottom.insert(Tile::Numbered(i)); + top.insert(Tile::Numbered(p)); + } + } + + rules.push(CollapseRule { + tile: Tile::Numbered(i), + top, + right, + bottom, + left, + }); + } + + rules + } +} + +impl LandGenerator for WavefrontCollapseLandGenerator { + fn generate_land>( + &self, + parameters: &LandGenerationParameters, + random_numbers: &mut I, + ) -> land2d::Land2D { + let tiles = self.load_template(parameters); + let rules = self.build_rules(&tiles); + + let mut wfc = WavefrontCollapse::new(self.template.wrap); + wfc.set_rules(rules); + + let wfc_size = if let Some(first_tile) = tiles.first() { + let tile_size = first_tile.size(); + + Size::new( + self.template.size.width / tile_size.width, + self.template.size.height / tile_size.height, + ) + } else { + Size::new(1, 1) + }; + + wfc.generate_map(&wfc_size, |_| {}, random_numbers); + + // render tiles into resulting land array + let mut result = land2d::Land2D::new(&self.template.size, parameters.zero); + let offset_y = result.height() - result.play_height(); + let offset_x = (result.width() - result.play_width()) / 2; + + for row in 0..wfc_size.height { + for column in 0..wfc_size.width { + if let Some(Tile::Numbered(tile_index)) = wfc.grid().get(row, column) { + let tile = &tiles[*tile_index]; + + for tile_row in 0..tile.size().height { + for tile_column in 0..tile.size().width { + result.map( + (row * tile.size().height + tile_row + offset_y) as i32, + (column * tile.size().width + tile_column + offset_x) as i32, + |p| { + *p = + *tile.get(tile_row, tile_column).unwrap_or(¶meters.zero) + }, + ); + } + } + } + } + } + + result + } +} + +impl From<&EdgeDescription> for Edge { + fn from(val: &EdgeDescription) -> Self { + let edge = Edge::new(val.name.clone(), val.symmetrical.unwrap_or_default()); + + if val.reversed.unwrap_or_default() { + edge.reversed() + } else { + edge + } + } +} diff -r 2b4f361e3891 -r 629d5123a979 rust/landgen/src/wavefront_collapse/mod.rs --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/rust/landgen/src/wavefront_collapse/mod.rs Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,4 @@ +pub mod generator; +mod tile_image; +mod transform; +mod wavefront_collapse; diff -r 2b4f361e3891 -r 629d5123a979 rust/landgen/src/wavefront_collapse/tile_image.rs --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/rust/landgen/src/wavefront_collapse/tile_image.rs Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,242 @@ +use super::transform::Transform; +use integral_geometry::Size; +use std::rc::Rc; +use vec2d::Vec2D; + +#[derive(PartialEq, Clone, Debug)] +pub struct Edge { + id: I, + symmetrical: bool, + reverse: bool, +} + +impl Edge { + #[inline] + pub fn new(id: I, symmetrical: bool) -> Self { + Self { + id, + symmetrical, + reverse: false, + } + } + + #[inline] + pub fn reversed(&self) -> Self { + Self { + id: self.id.clone(), + symmetrical: self.symmetrical, + reverse: !self.symmetrical && !self.reverse, + } + } + + #[inline] + pub fn is_compatible(&self, other: &Self) -> bool { + self.id == other.id && ((self.reverse != other.reverse) || self.symmetrical) + } +} + +#[derive(Clone)] +pub struct TileImage { + image: Rc>, + pub transform: Transform, + top: Edge, + right: Edge, + bottom: Edge, + left: Edge, +} + +impl TileImage { + pub fn new( + image: Vec2D, + top: Edge, + right: Edge, + bottom: Edge, + left: Edge, + ) -> Self { + Self { + image: Rc::new(image), + transform: Transform::default(), + top, + right, + bottom, + left, + } + } + + pub fn mirrored(&self) -> Self { + Self { + image: self.image.clone(), + transform: self.transform.mirror(), + top: self.top.reversed(), + right: self.left.reversed(), + bottom: self.bottom.reversed(), + left: self.right.reversed(), + } + } + + pub fn flipped(&self) -> Self { + Self { + image: self.image.clone(), + transform: self.transform.flip(), + top: self.bottom.reversed(), + right: self.right.reversed(), + bottom: self.top.reversed(), + left: self.left.reversed(), + } + } + + pub fn rotated90(&self) -> Self { + Self { + image: self.image.clone(), + transform: self.transform.rotate90(), + top: self.left.clone(), + right: self.top.clone(), + bottom: self.right.clone(), + left: self.bottom.clone(), + } + } + + pub fn rotated180(&self) -> Self { + Self { + image: self.image.clone(), + transform: self.transform.rotate180(), + top: self.bottom.clone(), + right: self.left.clone(), + bottom: self.top.clone(), + left: self.right.clone(), + } + } + + pub fn rotated270(&self) -> Self { + Self { + image: self.image.clone(), + transform: self.transform.rotate270(), + top: self.right.clone(), + right: self.bottom.clone(), + bottom: self.left.clone(), + left: self.top.clone(), + } + } + + #[inline] + pub fn right_edge(&self) -> &Edge { + &self.right + } + + #[inline] + pub fn bottom_edge(&self) -> &Edge { + &self.bottom + } + + #[inline] + pub fn left_edge(&self) -> &Edge { + &self.left + } + + #[inline] + pub fn top_edge(&self) -> &Edge { + &self.top + } + + #[inline] + pub fn size(&self) -> Size { + match self.transform { + Transform::Rotate0(_) => self.image.size(), + Transform::Rotate90(_) => Size::new(self.image.size().height, self.image.size().width), + } + } + + #[inline] + pub fn get(&self, row: usize, column: usize) -> Option<&T> { + match self.transform { + Transform::Rotate0(_) => { + let image_row = if self.transform.is_flipped() { + self.image.height().wrapping_sub(1).wrapping_sub(row) + } else { + row + }; + + let image_column = if self.transform.is_mirrored() { + self.image.width().wrapping_sub(1).wrapping_sub(column) + } else { + column + }; + + self.image.get(image_row, image_column) + } + Transform::Rotate90(_) => { + let image_row = if self.transform.is_mirrored() { + column + } else { + self.image.height().wrapping_sub(1).wrapping_sub(column) + }; + + let image_column = if self.transform.is_flipped() { + self.image.width().wrapping_sub(1).wrapping_sub(row) + } else { + row + }; + + self.image.get(image_row, image_column) + } + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_edge_new() { + let edge = Edge::new(1, true); + assert_eq!(edge.id, 1); + assert_eq!(edge.symmetrical, true); + assert_eq!(edge.reverse, false); + } + + #[test] + fn test_edge_reversed() { + let edge = Edge::new(1, true); + let reversed = edge.reversed(); + assert_eq!(reversed.id, edge.id); + assert_eq!(reversed.symmetrical, edge.symmetrical); + assert_eq!(reversed.reverse, false); + + let edge = Edge::new(1, false); + let reversed = edge.reversed(); + assert_eq!(reversed.id, edge.id); + assert_eq!(reversed.symmetrical, edge.symmetrical); + assert_eq!(reversed.reverse, true); + } + + #[test] + fn test_edge_equality() { + let edge1 = Edge::new(1, true); + let edge2 = Edge::new(1, true); + assert_eq!(edge1, edge2); + + let edge1 = Edge::new(1, false); + let edge2 = Edge::new(1, false); + assert_eq!(edge1, edge2); + + let edge1 = Edge::new(1, false); + let edge2 = Edge::new(2, false); + assert_ne!(edge1, edge2); + } + + #[test] + fn test_edge_equality_with_reverse() { + let edge1 = Edge::new(1, true); + let edge2 = edge1.reversed(); + assert_eq!(edge1, edge2); + + let edge1 = Edge::new(1, false); + let edge2 = edge1.reversed(); + assert_ne!(edge1, edge2); + + let edge1 = Edge::new(1, true); + let edge2 = edge1.reversed().reversed(); + assert_eq!(edge1, edge2); + } +} diff -r 2b4f361e3891 -r 629d5123a979 rust/landgen/src/wavefront_collapse/transform.rs --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/rust/landgen/src/wavefront_collapse/transform.rs Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,214 @@ +#[derive(Debug, PartialEq, Clone, Copy)] +pub enum SymmetryTransform { + Id, + Flip, + Mirror, + FlipMirror, +} + +#[derive(Debug, PartialEq, Clone, Copy)] +pub enum Transform { + Rotate0(SymmetryTransform), + Rotate90(SymmetryTransform), +} + +impl Default for Transform { + fn default() -> Self { + Transform::Rotate0(SymmetryTransform::Id) + } +} + +impl SymmetryTransform { + pub fn mirror(&self) -> Self { + use SymmetryTransform::*; + match self { + Id => Mirror, + Flip => FlipMirror, + Mirror => Id, + FlipMirror => Flip, + } + } + + pub fn flip(&self) -> Self { + use SymmetryTransform::*; + match self { + Id => Flip, + Flip => Id, + Mirror => FlipMirror, + FlipMirror => Mirror, + } + } + + pub fn is_mirrored(&self) -> bool { + use SymmetryTransform::*; + match self { + Id => false, + Flip => false, + Mirror => true, + FlipMirror => true, + } + } + + pub fn is_flipped(&self) -> bool { + use SymmetryTransform::*; + match self { + Id => false, + Flip => true, + Mirror => false, + FlipMirror => true, + } + } +} + +impl Transform { + pub fn new() -> Self { + Self::default() + } + + pub fn mirror(self) -> Transform { + match self { + Transform::Rotate0(s) => Transform::Rotate0(s.mirror()), + Transform::Rotate90(s) => Transform::Rotate90(s.flip()), + } + } + + pub fn flip(self) -> Transform { + match self { + Transform::Rotate0(s) => Transform::Rotate0(s.flip()), + Transform::Rotate90(s) => Transform::Rotate90(s.mirror()), + } + } + + pub fn rotate90(self) -> Transform { + match self { + Transform::Rotate0(s) => Transform::Rotate90(s), + Transform::Rotate90(s) => Transform::Rotate0(s.flip().mirror()), + } + } + + pub fn rotate180(self) -> Transform { + match self { + Transform::Rotate0(s) => Transform::Rotate0(s.flip().mirror()), + Transform::Rotate90(s) => Transform::Rotate90(s.flip().mirror()), + } + } + + pub fn rotate270(self) -> Transform { + match self { + Transform::Rotate0(s) => Transform::Rotate90(s.flip().mirror()), + Transform::Rotate90(s) => Transform::Rotate0(s), + } + } + + pub fn is_mirrored(&self) -> bool { + match self { + Transform::Rotate0(s) => s.is_mirrored(), + Transform::Rotate90(s) => s.is_mirrored(), + } + } + + pub fn is_flipped(&self) -> bool { + match self { + Transform::Rotate0(s) => s.is_flipped(), + Transform::Rotate90(s) => s.is_flipped(), + } + } +} + +#[cfg(test)] +mod tests { + use super::{SymmetryTransform::*, Transform::*, *}; + + // I totally wrote all of this myself and didn't use ChatGPT + #[test] + fn test_default() { + let rt = Transform::new(); + assert_eq!(rt, Rotate0(Id)); + } + + #[test] + fn test_mirror() { + let rt = Rotate90(Flip); + let mirrored = rt.mirror(); + assert_eq!(mirrored, Rotate90(Id)); + } + + #[test] + fn test_flip() { + let rt = Transform::new().rotate180().mirror(); + let flipped = rt.flip(); + assert_eq!(flipped, Rotate0(Id)); + } + + #[test] + fn test_rotate90() { + let rt = Rotate0(Id); + let rotated = rt.rotate90(); + assert_eq!(rotated, Rotate90(Id)); + } + + #[test] + fn test_rotate180() { + let rt = Rotate90(Mirror); + let rotated = rt.rotate180(); + assert_eq!(rotated, Rotate90(Flip)); + } + + #[test] + fn test_rotate270() { + let rt = Transform::new().rotate180().flip(); + let rotated = rt.rotate270(); + assert_eq!(rotated, Rotate90(Flip)); + } + + #[test] + fn test_rotate180_2() { + let rt = Transform::new().rotate180(); + assert_eq!(rt, Rotate0(FlipMirror)); + } + + #[test] + fn test_rotation_chain() { + assert_eq!( + Transform::default(), + Transform::default() + .rotate90() + .rotate90() + .rotate90() + .rotate90() + ); + assert_eq!( + Transform::default().rotate90(), + Transform::default().rotate180().rotate90().rotate180() + ); + assert_eq!( + Transform::default().rotate180(), + Transform::default().rotate180().rotate270().rotate90() + ); + } + + #[test] + fn test_combinations_chain() { + assert_eq!( + Transform::default(), + Transform::default().flip().rotate180().flip().rotate180() + ); + assert_eq!( + Transform::default(), + Transform::default() + .mirror() + .rotate180() + .mirror() + .rotate180() + ); + assert_eq!( + Transform::default(), + Transform::default() + .rotate90() + .flip() + .rotate90() + .mirror() + .rotate180() + ); + } +} diff -r 2b4f361e3891 -r 629d5123a979 rust/landgen/src/wavefront_collapse/wavefront_collapse.rs --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/rust/landgen/src/wavefront_collapse/wavefront_collapse.rs Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,187 @@ +use integral_geometry::Size; +use std::collections::HashSet; +use vec2d::Vec2D; + +#[derive(PartialEq, Eq, Hash, Clone, Copy, Debug)] +pub enum Tile { + Empty, + Outside, + Numbered(usize), +} + +impl Default for Tile { + fn default() -> Self { + Tile::Outside + } +} + +#[derive(Debug)] +pub struct CollapseRule { + pub tile: Tile, + pub right: HashSet, + pub bottom: HashSet, + pub left: HashSet, + pub top: HashSet, +} + +pub struct WavefrontCollapse { + rules: Vec, + grid: Vec2D, + wrap: bool, +} + +impl Default for WavefrontCollapse { + fn default() -> Self { + Self { + rules: Vec::new(), + grid: Vec2D::new(&Size::new(1, 1), Tile::Empty), + wrap: false, + } + } +} + +impl WavefrontCollapse { + pub fn new(wrap: bool) -> Self { + Self { + rules: Vec::new(), + grid: Vec2D::new(&Size::new(1, 1), Tile::Empty), + wrap, + } + } + + pub fn generate_map, F: FnOnce(&mut Vec2D)>( + &mut self, + map_size: &Size, + seed_fn: F, + random_numbers: &mut I, + ) { + self.grid = Vec2D::new(map_size, Tile::Empty); + + seed_fn(&mut self.grid); + + while self.collapse_step(random_numbers) {} + } + + pub fn set_rules(&mut self, rules: Vec) { + self.rules = rules; + } + + fn get_tile(&self, y: usize, x: usize) -> Tile { + let x = if self.wrap { + if x == usize::MAX { + self.grid.width() - 1 + } else if x == self.grid.width() { + 0 + } else { + x + } + } else { + x + }; + + self.grid.get(y, x).copied().unwrap_or_default() + } + + fn collapse_step>(&mut self, random_numbers: &mut I) -> bool { + let mut tiles_to_collapse = (usize::max_value(), Vec::new()); + + // Iterate through the tiles in the land + for x in 0..self.grid.width() { + for y in 0..self.grid.height() { + let current_tile = self.get_tile(y, x); + + if let Tile::Empty = current_tile { + // calc entropy + let right_tile = self.get_tile(y, x + 1); + let bottom_tile = self.get_tile(y + 1, x); + let left_tile = self.get_tile(y, x.wrapping_sub(1)); + let top_tile = self.get_tile(y.wrapping_sub(1), x); + + let possibilities: Vec = self + .rules + .iter() + .filter_map(|rule| { + if rule.right.contains(&right_tile) + && rule.bottom.contains(&bottom_tile) + && rule.left.contains(&left_tile) + && rule.top.contains(&top_tile) + { + Some(rule.tile) + } else { + None + } + }) + .collect(); + + let entropy = possibilities.len(); + if entropy > 0 { + if entropy <= tiles_to_collapse.0 { + let entry = ( + y, + x, + possibilities + [random_numbers.next().unwrap_or_default() as usize % entropy], + ); + + if entropy < tiles_to_collapse.0 { + tiles_to_collapse = (entropy, vec![entry]) + } else { + tiles_to_collapse.1.push(entry) + } + } + } else { + /*println!("We're here: {}, {}", x, y); + println!( + "Neighbour tiles are: {:?} {:?} {:?} {:?}", + right_tile, bottom_tile, left_tile, top_tile + ); + println!("Rules are: {:?}", self.rules);*/ + + //todo!("no collapse possible - what to do?") + } + } + } + } + + let tiles_to_collapse = tiles_to_collapse.1; + let possibilities_number = tiles_to_collapse.len(); + + if possibilities_number > 0 { + let (y, x, tile) = tiles_to_collapse + [random_numbers.next().unwrap_or_default() as usize % possibilities_number]; + + *self + .grid + .get_mut(y, x) + .expect("correct iteration over grid") = tile; + + true + } else { + false + } + } + + pub fn grid(&self) -> &Vec2D { + &self.grid + } +} + +#[cfg(test)] +mod tests { + use super::{Tile, WavefrontCollapse}; + use integral_geometry::Size; + use vec2d::Vec2D; + + #[test] + fn test_wavefront_collapse() { + let size = Size::new(4, 4); + let mut rnd = [0u32; 64].into_iter().cycle(); + let mut wfc = WavefrontCollapse::default(); + + wfc.generate_map(&size, |_| {}, &mut rnd); + + let empty_land = Vec2D::new(&size, Tile::Empty); + + assert_eq!(empty_land.as_slice(), wfc.grid().as_slice()); + } +} diff -r 2b4f361e3891 -r 629d5123a979 rust/lfprng/Cargo.toml --- a/rust/lfprng/Cargo.toml Sat Sep 28 22:27:13 2024 +0200 +++ b/rust/lfprng/Cargo.toml Tue Dec 31 15:19:43 2024 +0100 @@ -5,3 +5,4 @@ edition = "2018" [dependencies] +rand = "0.8" diff -r 2b4f361e3891 -r 629d5123a979 rust/lfprng/src/lib.rs --- a/rust/lfprng/src/lib.rs Sat Sep 28 22:27:13 2024 +0200 +++ b/rust/lfprng/src/lib.rs Tue Dec 31 15:19:43 2024 +0100 @@ -1,3 +1,5 @@ +use rand::{Error, RngCore, SeedableRng}; + pub struct LaggedFibonacciPRNG { circular_buffer: [u32; 64], index: usize, @@ -5,7 +7,7 @@ impl LaggedFibonacciPRNG { pub fn new(init_values: &[u8]) -> Self { - let mut buf = [0xa98765 + 68; 64]; + let mut buf = [0xa98765; 64]; for i in 0..std::cmp::min(init_values.len(), 54) { buf[i] = init_values[i] as u32; @@ -30,10 +32,17 @@ #[inline] fn get_next(&mut self) -> u32 { + const PRIME_NUM: u32 = 2147483629; + self.index = (self.index + 1) & 0x3f; - self.circular_buffer[self.index] = (self.circular_buffer[(self.index + 40) & 0x3f] - + self.circular_buffer[(self.index + 9) & 0x3f]) - & 0x7fffffff; + let next_value = self.circular_buffer[(self.index + 40) & 0x3f] + + self.circular_buffer[(self.index + 9) & 0x3f]; + + self.circular_buffer[self.index] = if next_value > PRIME_NUM { + next_value - PRIME_NUM + } else { + next_value + }; self.circular_buffer[self.index] } @@ -60,6 +69,32 @@ } } +impl RngCore for LaggedFibonacciPRNG { + fn next_u32(&mut self) -> u32 { + self.get_next().wrapping_add(self.get_next()) + } + + fn next_u64(&mut self) -> u64 { + ((self.next_u32() as u64) << 32) | self.next_u32() as u64 + } + + fn fill_bytes(&mut self, dest: &mut [u8]) { + dest.iter_mut().for_each(|x| *x = self.next_u32() as u8); + } + + fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> { + Ok(self.fill_bytes(dest)) + } +} + +impl SeedableRng for LaggedFibonacciPRNG { + type Seed = [u8; 32]; + + fn from_seed(seed: Self::Seed) -> Self { + LaggedFibonacciPRNG::new(&seed) + } +} + #[cfg(test)] #[test] fn compatibility() { diff -r 2b4f361e3891 -r 629d5123a979 rust/lib-hedgewars-engine/src/instance.rs --- a/rust/lib-hedgewars-engine/src/instance.rs Sat Sep 28 22:27:13 2024 +0200 +++ b/rust/lib-hedgewars-engine/src/instance.rs Tue Dec 31 15:19:43 2024 +0100 @@ -5,7 +5,7 @@ use hedgewars_engine_messages::queue::*; use integral_geometry::{Point, Rect, Size}; -use landgen::outline_template::OutlineTemplate; +use landgen::outline_template_based::outline_template::OutlineTemplate; use std::path::Path; diff -r 2b4f361e3891 -r 629d5123a979 rust/lib-hedgewars-engine/src/lib.rs --- a/rust/lib-hedgewars-engine/src/lib.rs Sat Sep 28 22:27:13 2024 +0200 +++ b/rust/lib-hedgewars-engine/src/lib.rs Tue Dec 31 15:19:43 2024 +0100 @@ -5,7 +5,7 @@ mod world; use std::{ - ffi::{CString, CStr}, + ffi::{CStr, CString}, io::{Read, Write}, mem::replace, os::raw::{c_char, c_void}, diff -r 2b4f361e3891 -r 629d5123a979 rust/lib-hedgewars-engine/src/render/gear.rs --- a/rust/lib-hedgewars-engine/src/render/gear.rs Sat Sep 28 22:27:13 2024 +0200 +++ b/rust/lib-hedgewars-engine/src/render/gear.rs Tue Dec 31 15:19:43 2024 +0100 @@ -71,22 +71,10 @@ } const SPRITE_LOAD_LIST: &[(SpriteId, &str)] = &[ - ( - SpriteId::Mine, - "Graphics/MineOn.png", - ), - ( - SpriteId::Grenade, - "Graphics/Bomb.png", - ), - ( - SpriteId::Cheese, - "Graphics/cheese.png", - ), - ( - SpriteId::Cleaver, - "Graphics/cleaver.png", - ), + (SpriteId::Mine, "Graphics/MineOn.png"), + (SpriteId::Grenade, "Graphics/Bomb.png"), + (SpriteId::Cheese, "Graphics/cheese.png"), + (SpriteId::Cleaver, "Graphics/cleaver.png"), ]; const MAX_SPRITES: usize = SpriteId::MaxSprite as usize + 1; diff -r 2b4f361e3891 -r 629d5123a979 rust/lib-hedgewars-engine/src/world.rs --- a/rust/lib-hedgewars-engine/src/world.rs Sat Sep 28 22:27:13 2024 +0200 +++ b/rust/lib-hedgewars-engine/src/world.rs Tue Dec 31 15:19:43 2024 +0100 @@ -7,8 +7,9 @@ use integral_geometry::{Point, Rect, Size}; use land2d::Land2D; use landgen::{ - outline_template::OutlineTemplate, template_based::TemplatedLandGenerator, - LandGenerationParameters, LandGenerator, + outline_template_based::outline_template::OutlineTemplate, + outline_template_based::template_based::TemplatedLandGenerator, LandGenerationParameters, + LandGenerator, }; use lfprng::LaggedFibonacciPRNG; use std::path::{Path, PathBuf}; @@ -69,9 +70,16 @@ use mapgen::{theme::Theme, MapGenerator}; if let Some(ref state) = self.game_state { + self.camera.position = state.land.play_box().center(); + + let parameters = LandGenerationParameters::new(0u32, 0x8000u32, 0, false, false); let theme = Theme::load(self.data_path.join(Path::new("Themes/Cheese/")).as_path()).unwrap(); - let texture = MapGenerator::new().make_texture(&state.land, &theme); + let texture = MapGenerator::::new().make_texture( + &state.land, + ¶meters, + &theme, + ); if let Some(ref mut renderer) = self.map_renderer { renderer.init(&texture); } diff -r 2b4f361e3891 -r 629d5123a979 rust/lib-hwengine-future/Cargo.toml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/rust/lib-hwengine-future/Cargo.toml Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,18 @@ +[package] +name = "lib-hwengine-future" +version = "0.1.0" +edition = "2021" +authors = ["Andrey Korotaev "] + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +land2d = { path = "../land2d" } +integral-geometry = { path = "../integral-geometry" } +mapgen = { path = "../mapgen" } +landgen = { path = "../landgen" } +lfprng = { path = "../lfprng" } + +[lib] +name = "hwengine_future" +crate-type = ["cdylib"] diff -r 2b4f361e3891 -r 629d5123a979 rust/lib-hwengine-future/src/ai/action.rs --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/rust/lib-hwengine-future/src/ai/action.rs Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,29 @@ +#[derive(Clone)] +pub enum Direction { + Left, + Right, +} +#[derive(Clone)] +pub enum Action { + Walk(Direction), + LongJump, + HighJump(usize), +} + +pub struct Actions { + actions: Vec, +} + +impl Actions { + pub fn new() -> Self { + Self { actions: vec![] } + } + + pub fn push(&mut self, action: Action) { + self.actions.push(action) + } + + pub fn pop(&mut self) -> Option { + self.actions.pop() + } +} diff -r 2b4f361e3891 -r 629d5123a979 rust/lib-hwengine-future/src/ai/ammo.rs --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/rust/lib-hwengine-future/src/ai/ammo.rs Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,77 @@ +#[repr(usize)] +pub enum AmmoType { + Nothing, + Grenade, + ClusterBomb, + Bazooka, + Bee, + Shotgun, + PickHammer, // 6 + Skip, + Rope, + Mine, + DEagle, + Dynamite, + FirePunch, + Whip, // 13 + BaseballBat, + Parachute, + AirAttack, + MineStrike, + BlowTorch, // 18 + Girder, + Teleport, + Switch, + Mortar, + Kamikaze, + Cake, // 24 + Seduction, + Watermelon, + HellishBomb, + Napalm, + Drill, + Ballgun, // 30 + RCPlane, + LowGravity, + ExtraDamage, + Invulnerable, + ExtraTime, // 35 + LaserSight, + Vampiric, + SniperRifle, + Jetpack, + Molotov, + Birdy, + PortalGun, // 42 + Piano, + GasBomb, + SineGun, + Flamethrower, + SMine, + Hammer, // 48 + Resurrector, + DrillStrike, + Snowball, + Tardis, + LandGun, // 53 + IceGun, + Knife, + Rubber, + AirMine, + Creeper, + Minigun, + Sentry, // 60 + Count, +} + +impl TryFrom for AmmoType { + type Error = &'static str; + + fn try_from(value: usize) -> Result { + if value < Self::Count as usize { + Ok(unsafe { std::mem::transmute(value) }) + } else { + Err("Invalid ammo type") + } + } +} diff -r 2b4f361e3891 -r 629d5123a979 rust/lib-hwengine-future/src/ai/attack_tests.rs --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/rust/lib-hwengine-future/src/ai/attack_tests.rs Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,20 @@ +use crate::ai::ammo::AmmoType; +use crate::ai::Target; +use crate::GameField; + +fn analyze_grenade(game_field: &GameField, targets: &[Target], my_x: f32, my_y: f32) {} + +impl AmmoType { + pub(crate) fn analyze_attacks( + &self, + game_field: &GameField, + targets: &[Target], + my_x: f32, + my_y: f32, + ) { + match self { + AmmoType::Grenade => analyze_grenade(game_field, targets, my_x, my_y), + _ => {} + } + } +} diff -r 2b4f361e3891 -r 629d5123a979 rust/lib-hwengine-future/src/ai/mod.rs --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/rust/lib-hwengine-future/src/ai/mod.rs Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,119 @@ +mod action; +pub mod ammo; +mod attack_tests; + +use crate::GameField; +use action::*; +use integral_geometry::Point; +use std::collections::HashMap; + +pub struct Target { + point: Point, + health: i32, + radius: u32, + density: f32, +} + +pub struct Hedgehog { + pub(crate) x: f32, + pub(crate) y: f32, + pub(crate) ammo: [u32; ammo::AmmoType::Count as usize], +} + +pub struct AI<'a> { + game_field: &'a GameField, + ammo: [u32; ammo::AmmoType::Count as usize], + targets: Vec, + team: Vec, + planned_actions: Option, +} + +#[derive(Clone)] +pub(crate) struct Waypoint { + x: f32, + y: f32, + ticks: usize, + damage: usize, + previous_point: Option<(usize, Action)>, +} + +#[derive(Default)] +pub(crate) struct Waypoints { + key_points: Vec, + points: HashMap, +} + +impl Waypoints { + fn add_keypoint(&mut self, waypoint: Waypoint) { + let [x, y] = [waypoint.x, waypoint.y].map(|i| i as i32); + let point = Point::new(x, y); + self.key_points.push(waypoint.clone()); + self.points.insert(point, waypoint); + } +} + +impl IntoIterator for Waypoints { + type Item = Waypoint; + type IntoIter = std::collections::hash_map::IntoValues; + + fn into_iter(self) -> Self::IntoIter { + self.points.into_values() + } +} + +impl<'a> AI<'a> { + pub fn new(game_field: &'a GameField) -> AI<'a> { + Self { + game_field, + ammo: [0; ammo::AmmoType::Count as usize], + targets: vec![], + team: vec![], + planned_actions: None, + } + } + + pub fn set_available_ammo(&mut self, ammo: [u32; ammo::AmmoType::Count as usize]) { + self.ammo = ammo; + } + + pub fn get_team_mut(&mut self) -> &mut Vec { + &mut self.team + } + + pub fn walk(&self, hedgehog: &Hedgehog) { + let mut stack = Vec::::new(); + let mut waypoints = Waypoints::default(); + + waypoints.add_keypoint(Waypoint { + x: hedgehog.x, + y: hedgehog.y, + ticks: 0, + damage: 0, + previous_point: None, + }); + + while let Some(waypoint) = stack.pop() { + // find other positions + } + + for Waypoint { x, y, .. } in waypoints { + self.analyze_position_attacks(x, y); + } + } + + fn analyze_position_attacks(&self, x: f32, y: f32) { + self.ammo + .iter() + .enumerate() + .filter(|&(_, &count)| count > 0u32) + .for_each(|(a, &count)| { + let a = ammo::AmmoType::try_from(a).expect("What are you iterating over?"); + + a.analyze_attacks(self.game_field, &self.targets, x, y) + }); + } + + pub fn have_plan(&self) -> bool { + self.planned_actions.is_some() + } +} diff -r 2b4f361e3891 -r 629d5123a979 rust/lib-hwengine-future/src/lib.rs --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/rust/lib-hwengine-future/src/lib.rs Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,293 @@ +mod ai; + +use integral_geometry::{Point, Size}; + +use ai::*; +use landgen::{ + maze::MazeTemplate, outline_template_based::outline_template::OutlineTemplate, + wavefront_collapse::generator::TemplateDescription as WfcTemplate, LandGenerationParameters, + LandGenerator, +}; +use lfprng::LaggedFibonacciPRNG; +use mapgen::{theme::Theme, MapGenerator}; +use std::fs; +use std::ptr::slice_from_raw_parts; +use std::{ffi::CStr, path::Path}; + +#[repr(C)] +pub struct GameField { + collision: land2d::Land2D, + pixels: land2d::Land2D, + landgen_parameters: Option>, +} + +#[no_mangle] +pub extern "C" fn get_game_field_parameters( + game_field: &GameField, + width: &mut i32, + height: &mut i32, + play_width: &mut i32, + play_height: &mut i32, +) { + *width = game_field.collision.width() as i32; + *height = game_field.collision.height() as i32; + + *play_width = game_field.collision.play_width() as i32; + *play_height = game_field.collision.play_height() as i32; +} + +#[no_mangle] +pub extern "C" fn create_empty_game_field(width: u32, height: u32) -> *mut GameField { + let game_field = Box::new(GameField { + collision: land2d::Land2D::new(&Size::new(width as usize, height as usize), 0), + pixels: land2d::Land2D::new(&Size::new(width as usize, height as usize), 0), + landgen_parameters: None, + }); + + Box::into_raw(game_field) +} + +#[no_mangle] +pub unsafe extern "C" fn generate_outline_templated_game_field( + feature_size: u32, + seed: *const i8, + template_type: *const i8, + data_path: *const i8, +) -> *mut GameField { + let data_path: &str = CStr::from_ptr(data_path).to_str().unwrap(); + let data_path = Path::new(&data_path); + + let seed: &str = CStr::from_ptr(seed).to_str().unwrap(); + let template_type: &str = CStr::from_ptr(template_type).to_str().unwrap(); + + let mut random_numbers_gen = LaggedFibonacciPRNG::new(seed.as_bytes()); + + let yaml_templates = + fs::read_to_string(data_path.join(Path::new("map_templates.yaml")).as_path()) + .expect("Error reading map templates file"); + let mut map_gen = MapGenerator::::new(data_path); + map_gen.import_yaml_templates(&yaml_templates); + + let distance_divisor = feature_size.pow(2) / 8 + 10; + let params = LandGenerationParameters::new(0u16, 0x8000u16, distance_divisor, false, false); + let template = map_gen + .get_template(template_type, &mut random_numbers_gen) + .expect("Error reading outline templates file") + .clone(); + let landgen = map_gen.build_generator(template); + let collision = landgen.generate_land(¶ms, &mut random_numbers_gen); + let size = collision.size().size(); + + let game_field = Box::new(GameField { + collision, + pixels: land2d::Land2D::new(&size, 0), + landgen_parameters: Some(params), + }); + + Box::into_raw(game_field) +} + +#[no_mangle] +pub unsafe extern "C" fn generate_wfc_templated_game_field( + feature_size: u32, + seed: *const i8, + template_type: *const i8, + data_path: *const i8, +) -> *mut GameField { + let data_path: &str = CStr::from_ptr(data_path).to_str().unwrap(); + let data_path = Path::new(&data_path); + + let seed: &str = CStr::from_ptr(seed).to_str().unwrap(); + let template_type: &str = CStr::from_ptr(template_type).to_str().unwrap(); + + let mut random_numbers_gen = LaggedFibonacciPRNG::new(seed.as_bytes()); + + let yaml_templates = + fs::read_to_string(data_path.join(Path::new("wfc_templates.yaml")).as_path()) + .expect("Error reading map templates file"); + let mut map_gen = MapGenerator::::new(data_path); + map_gen.import_yaml_templates(&yaml_templates); + + let distance_divisor = feature_size.pow(2) / 8 + 10; + let params = LandGenerationParameters::new(0u16, 0x8000u16, distance_divisor, false, false); + let template = map_gen + .get_template(template_type, &mut random_numbers_gen) + .expect("Error reading wfc templates file") + .clone(); + let landgen = map_gen.build_generator(template); + let collision = landgen.generate_land(¶ms, &mut random_numbers_gen); + let size = collision.size().size(); + + let game_field = Box::new(GameField { + collision, + pixels: land2d::Land2D::new(&size, 0), + landgen_parameters: Some(params), + }); + + Box::into_raw(game_field) +} + +#[no_mangle] +pub unsafe extern "C" fn generate_maze_game_field( + feature_size: u32, + seed: *const i8, + template_type: *const i8, + data_path: *const i8, +) -> *mut GameField { + let data_path: &str = CStr::from_ptr(data_path).to_str().unwrap(); + let data_path = Path::new(&data_path); + + let seed: &str = CStr::from_ptr(seed).to_str().unwrap(); + let template_type: &str = CStr::from_ptr(template_type).to_str().unwrap(); + + let mut random_numbers_gen = LaggedFibonacciPRNG::new(seed.as_bytes()); + + let yaml_templates = + fs::read_to_string(data_path.join(Path::new("maze_templates.yaml")).as_path()) + .expect("Error reading map templates file"); + + let mut map_gen = MapGenerator::::new(data_path); + map_gen.import_yaml_templates(&yaml_templates); + + let distance_divisor = feature_size.pow(2) / 8 + 10; + let params = LandGenerationParameters::new(0u16, 0x8000u16, distance_divisor, false, false); + + let template = map_gen + .get_template(template_type, &mut random_numbers_gen) + .expect("Error reading maze templates file") + .clone(); + + let landgen = map_gen.build_generator(template); + let collision = landgen.generate_land(¶ms, &mut random_numbers_gen); + let size = collision.size().size(); + + let game_field = Box::new(GameField { + collision, + pixels: land2d::Land2D::new(&size, 0), + landgen_parameters: Some(params), + }); + + Box::into_raw(game_field) +} + +#[no_mangle] +pub unsafe extern "C" fn apply_theme( + game_field: &mut GameField, + data_path: *const i8, + theme_name: *const i8, +) { + let data_path: &str = CStr::from_ptr(data_path).to_str().unwrap(); + let data_path = Path::new(&data_path); + + let theme_name: &str = CStr::from_ptr(theme_name).to_str().unwrap(); + let map_gen = MapGenerator::<()>::new(data_path); + + let theme = Theme::load( + data_path + .join(Path::new("Themes")) + .join(Path::new(theme_name)) + .as_path(), + ) + .unwrap(); + + let params = game_field + .landgen_parameters + .expect("Land generator parameters specified"); + let pixels = map_gen.make_texture(&game_field.collision, ¶ms, &theme); + + game_field.pixels = pixels.into(); +} + +#[no_mangle] +pub extern "C" fn land_get(game_field: &GameField, x: i32, y: i32) -> u16 { + game_field.collision.get(y, x) +} + +#[no_mangle] +pub extern "C" fn land_set(game_field: &mut GameField, x: i32, y: i32, value: u16) { + game_field.collision.map(y, x, |p| *p = value); +} + +#[no_mangle] +pub extern "C" fn land_row(game_field: &mut GameField, row: i32) -> *mut u16 { + game_field.collision[row as usize].as_mut_ptr() +} + +#[no_mangle] +pub extern "C" fn land_fill( + game_field: &mut GameField, + x: i32, + y: i32, + border_value: u16, + fill_value: u16, +) { + game_field + .collision + .fill(Point::new(x, y), border_value, fill_value) +} + +#[no_mangle] +pub extern "C" fn land_pixel_get(game_field: &GameField, x: i32, y: i32) -> u32 { + game_field.pixels.get(y, x) +} + +#[no_mangle] +pub extern "C" fn land_pixel_set(game_field: &mut GameField, x: i32, y: i32, value: u32) { + game_field.pixels.map(y, x, |p| *p = value); +} + +#[no_mangle] +pub extern "C" fn land_pixel_row(game_field: &mut GameField, row: i32) -> *mut u32 { + game_field.pixels[row as usize].as_mut_ptr() +} + +#[no_mangle] +pub unsafe extern "C" fn dispose_game_field(game_field: *mut GameField) { + drop(Box::from_raw(game_field)); +} + +#[no_mangle] +pub extern "C" fn create_ai(game_field: &GameField) -> *mut AI { + Box::into_raw(Box::new(AI::new(game_field))) +} + +#[no_mangle] +pub extern "C" fn ai_clear_team(ai: &mut AI) { + *ai.get_team_mut() = vec![]; +} + +#[no_mangle] +pub unsafe extern "C" fn ai_add_team_hedgehog( + ai: &mut AI, + x: f32, + y: f32, + ammo_counts: *const u32, +) { + let ammo_counts = + &*slice_from_raw_parts(ammo_counts, crate::ai::ammo::AmmoType::Count as usize); + let ammo_counts = std::array::from_fn(|i| ammo_counts[i].clone()); + + ai.get_team_mut().push(Hedgehog { + x, + y, + ammo: ammo_counts, + }); +} + +#[no_mangle] +pub extern "C" fn ai_think(ai: &AI) {} + +#[no_mangle] +pub extern "C" fn ai_have_plan(ai: &AI) -> bool { + ai.have_plan() +} + +#[no_mangle] +pub extern "C" fn ai_get(ai: &AI) -> bool { + ai.have_plan() +} + +#[no_mangle] +pub unsafe extern "C" fn dispose_ai(ai: *mut AI) { + drop(Box::from_raw(ai)); +} diff -r 2b4f361e3891 -r 629d5123a979 rust/mapgen/Cargo.toml --- a/rust/mapgen/Cargo.toml Sat Sep 28 22:27:13 2024 +0200 +++ b/rust/mapgen/Cargo.toml Tue Dec 31 15:19:43 2024 +0100 @@ -11,7 +11,7 @@ lfprng = { path = "../lfprng" } integral-geometry = { path = "../integral-geometry" } -rand = "0.5" +rand = "0.8" serde = "1.0" serde_yaml = "0.8" serde_derive = "1.0" diff -r 2b4f361e3891 -r 629d5123a979 rust/mapgen/src/lib.rs --- a/rust/mapgen/src/lib.rs Sat Sep 28 22:27:13 2024 +0200 +++ b/rust/mapgen/src/lib.rs Tue Dec 31 15:19:43 2024 +0100 @@ -1,79 +1,28 @@ +mod template; pub mod theme; use self::theme::Theme; -use integral_geometry::{Point, Rect, Size}; -use land2d::Land2D; -use landgen::outline_template::OutlineTemplate; -use rand::{thread_rng, Rng}; -use serde_derive::Deserialize; -use serde_yaml; -use std::{borrow::Borrow, collections::hash_map::HashMap, mem::replace}; -use vec2d::Vec2D; - -#[derive(Deserialize)] -struct PointDesc { - x: u32, - y: u32, -} +use crate::template::outline::TemplateCollectionDesc as OutlineTemplateCollectionDesc; +use crate::template::wavefront_collapse::TemplateCollectionDesc as WfcTemplateCollectionDesc; +use crate::template::maze::TemplateCollectionDesc as MazeTemplateCollectionDesc; -#[derive(Deserialize)] -struct RectDesc { - x: u32, - y: u32, - w: u32, - h: u32, -} - -#[derive(Deserialize)] -struct TemplateDesc { - width: usize, - height: usize, - can_flip: bool, - can_invert: bool, - can_mirror: bool, - is_negative: bool, - put_girders: bool, - max_hedgehogs: u8, - outline_points: Vec>, - fill_points: Vec, -} +use std::path::{Path, PathBuf}; -#[derive(Deserialize)] -struct TemplateCollectionDesc { - templates: Vec, - template_types: HashMap>, -} +use land2d::Land2D; +use landgen::{ + outline_template_based::{ + outline_template::OutlineTemplate, template_based::TemplatedLandGenerator, + }, + wavefront_collapse::generator::{ + TemplateDescription as WfcTemplate, WavefrontCollapseLandGenerator, + }, + maze::{MazeTemplate, MazeLandGenerator}, + LandGenerationParameters, LandGenerator, +}; +use rand::{seq::SliceRandom, Rng}; -impl From<&TemplateDesc> for OutlineTemplate { - fn from(desc: &TemplateDesc) -> Self { - OutlineTemplate { - islands: desc - .outline_points - .iter() - .map(|v| { - v.iter() - .map(|r| { - Rect::from_size( - Point::new(r.x as i32, r.y as i32), - Size::new(r.w as usize, r.h as usize), - ) - }) - .collect() - }) - .collect(), - fill_points: desc - .fill_points - .iter() - .map(|p| Point::new(p.x as i32, p.y as i32)) - .collect(), - size: Size::new(desc.width, desc.height), - can_flip: desc.can_flip, - can_invert: desc.can_invert, - can_mirror: desc.can_mirror, - is_negative: desc.is_negative, - } - } -} +use std::{borrow::Borrow, collections::hash_map::HashMap}; +use vec2d::Vec2D; #[derive(PartialEq, Eq, Hash, Clone, Debug)] struct TemplateType(String); @@ -85,43 +34,35 @@ } #[derive(Debug)] -pub struct MapGenerator { - pub(crate) templates: HashMap>, +pub struct MapGenerator { + pub(crate) templates: HashMap>, + data_path: PathBuf, } -impl MapGenerator { - pub fn new() -> Self { +impl MapGenerator { + pub fn new(data_path: &Path) -> Self { Self { templates: HashMap::new(), + data_path: data_path.to_owned(), } } - pub fn import_yaml_templates(&mut self, text: &str) { - let mut desc: TemplateCollectionDesc = serde_yaml::from_str(text).unwrap(); - let templates = replace(&mut desc.templates, vec![]); - self.templates = desc - .template_types - .into_iter() - .map(|(size, indices)| { - ( - TemplateType(size), - indices.iter().map(|i| (&templates[*i]).into()).collect(), - ) - }) - .collect(); + pub fn get_template(&self, template_type: &str, rng: &mut R) -> Option<&T> { + self.templates + .get(template_type) + .and_then(|t| t.as_slice().choose(rng)) } - pub fn get_template(&self, template_type: &str) -> Option<&OutlineTemplate> { - self.templates - .get(template_type) - .and_then(|t| thread_rng().choose(t)) - } - - pub fn make_texture(&self, land: &Land2D, theme: &Theme) -> Vec2D + pub fn make_texture( + &self, + land: &Land2D, + parameters: &LandGenerationParameters, + theme: &Theme, + ) -> Vec2D where LandT: Copy + Default + PartialEq, { - let mut texture = Vec2D::new(land.size().size(), 0); + let mut texture = Vec2D::new(&land.size().size(), 0); if let Some(land_sprite) = theme.land_texture() { for (row_index, (land_row, tex_row)) in land.rows().zip(texture.rows_mut()).enumerate() @@ -131,6 +72,7 @@ while sprite_row.len() < land.width() - x_offset { let copy_range = x_offset..x_offset + sprite_row.len(); tex_row_copy( + parameters.basic(), &land_row[copy_range.clone()], &mut tex_row[copy_range], sprite_row, @@ -142,6 +84,7 @@ if x_offset < land.width() { let final_range = x_offset..land.width(); tex_row_copy( + parameters.basic(), &land_row[final_range.clone()], &mut tex_row[final_range], &sprite_row[..land.width() - x_offset], @@ -158,6 +101,7 @@ let mut offsets = vec![255u8; land.width()]; land_border_pass( + parameters.basic(), land.rows().rev().zip(texture.rows_mut().rev()), &mut offsets, border_width, @@ -170,6 +114,7 @@ offsets.iter_mut().for_each(|v| *v = 255); land_border_pass( + parameters.basic(), land.rows().zip(texture.rows_mut()), &mut offsets, border_width, @@ -181,13 +126,87 @@ } } +impl MapGenerator { + pub fn import_yaml_templates(&mut self, text: &str) { + let mut desc: OutlineTemplateCollectionDesc = serde_yaml::from_str(text).unwrap(); + let templates = std::mem::take(&mut desc.templates); + self.templates = desc + .template_types + .into_iter() + .map(|(size, indices)| { + ( + TemplateType(size), + indices + .indices + .iter() + .map(|i| Into::::into(templates[*i].clone())) + .map(|o| { + if indices.force_invert == Some(true) { + o.cavern() + } else { + o + } + }) + .collect(), + ) + }) + .collect(); + } + + pub fn build_generator(&self, template: OutlineTemplate) -> impl LandGenerator { + TemplatedLandGenerator::new(template) + } +} + +impl MapGenerator { + pub fn import_yaml_templates(&mut self, text: &str) { + let mut desc: WfcTemplateCollectionDesc = serde_yaml::from_str(text).unwrap(); + let templates = std::mem::take(&mut desc.templates); + self.templates = desc + .template_types + .into_iter() + .map(|(size, indices)| { + ( + TemplateType(size), + indices.iter().map(|i| (&templates[*i]).into()).collect(), + ) + }) + .collect(); + } + + pub fn build_generator(&self, template: WfcTemplate) -> impl LandGenerator { + WavefrontCollapseLandGenerator::new(template, &self.data_path) + } +} + +impl MapGenerator { + pub fn import_yaml_templates(&mut self, text: &str) { + let mut desc: MazeTemplateCollectionDesc = serde_yaml::from_str(text).unwrap(); + let templates = std::mem::take(&mut desc.templates); + self.templates = desc + .template_types + .into_iter() + .map(|(size, indices)| { + ( + TemplateType(size), + indices.iter().map(|i| (&templates[*i]).into()).collect(), + ) + }) + .collect(); + } + + pub fn build_generator(&self, template: MazeTemplate) -> impl LandGenerator { + MazeLandGenerator::new(template) + } +} + #[derive(Debug, Clone, Copy, PartialEq, Eq)] struct Color(u32); impl Color { #[inline] fn red(self) -> u8 { - (self.0 >> 0 & 0xFF) as u8 + (self.0 & 0xFF) as u8 } #[inline] @@ -219,11 +238,16 @@ let red = lerp(target.red(), source.red(), source.alpha()); let green = lerp(target.green(), source.green(), source.alpha()); let blue = lerp(target.blue(), source.blue(), source.alpha()); - (red as u32) << 0 | (green as u32) << 8 | (blue as u32) << 16 | (alpha as u32) << 24 + (red as u32) | (green as u32) << 8 | (blue as u32) << 16 | (alpha as u32) << 24 } -fn land_border_pass<'a, LandT, T, F>(rows: T, offsets: &mut [u8], border_width: u8, pixel_getter: F) -where +fn land_border_pass<'a, LandT, T, F>( + basic_value: LandT, + rows: T, + offsets: &mut [u8], + border_width: u8, + pixel_getter: F, +) where LandT: Default + PartialEq + 'a, T: Iterator, F: (Fn(usize, usize) -> u32), @@ -235,7 +259,7 @@ .zip(offsets.iter_mut()) .enumerate() { - *offset_v = if *land_v == LandT::default() { + *offset_v = if *land_v == basic_value { if *offset_v < border_width { *tex_v = blend(pixel_getter(x, *offset_v as usize), *tex_v) } @@ -247,22 +271,24 @@ } } -fn tex_row_copy(land_row: &[LandT], tex_row: &mut [u32], sprite_row: &[u32]) -where +fn tex_row_copy( + basic_value: LandT, + land_row: &[LandT], + tex_row: &mut [u32], + sprite_row: &[u32], +) where LandT: Default + PartialEq, { for ((land_v, tex_v), sprite_v) in land_row.iter().zip(tex_row.iter_mut()).zip(sprite_row) { - *tex_v = if *land_v == LandT::default() { - *sprite_v - } else { - 0 - } + *tex_v = if *land_v == basic_value { *sprite_v } else { 0 } } } #[cfg(test)] mod tests { - use crate::{MapGenerator, TemplateType}; + use std::path::Path; + use crate::{MapGenerator, OutlineTemplate, TemplateType}; + use rand::thread_rng; #[test] fn simple_load() { @@ -293,17 +319,18 @@ - {x: 1023, y: 0} template_types: - test: [0] + test: + indices: [0] "#; - let mut generator = MapGenerator::new(); + let mut generator = MapGenerator::::new(Path::new("")); generator.import_yaml_templates(&text); assert!(generator .templates .contains_key(&TemplateType("test".to_string()))); - let template = generator.get_template("test").unwrap(); + let template = generator.get_template("test", &mut thread_rng()).unwrap(); assert_eq!(template.islands[0].len(), 7); } diff -r 2b4f361e3891 -r 629d5123a979 rust/mapgen/src/template/maze.rs --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/rust/mapgen/src/template/maze.rs Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,33 @@ +use landgen::maze::MazeTemplate; +use serde_derive::Deserialize; + +use std::collections::hash_map::HashMap; +#[derive(Deserialize)] +pub struct TemplateDesc { + width: usize, + height: usize, + max_hedgehogs: u8, + cell_size: usize, + distortion_limiting_factor: u32, + braidness: u32, + invert: bool, +} + +#[derive(Deserialize)] +pub struct TemplateCollectionDesc { + pub templates: Vec, + pub template_types: HashMap>, +} + +impl From<&TemplateDesc> for MazeTemplate { + fn from(desc: &TemplateDesc) -> Self { + MazeTemplate { + width: desc.width, + height: desc.height, + cell_size: desc.cell_size, + inverted: desc.invert, + distortion_limiting_factor: desc.distortion_limiting_factor, + braidness: desc.braidness, + } + } +} diff -r 2b4f361e3891 -r 629d5123a979 rust/mapgen/src/template/mod.rs --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/rust/mapgen/src/template/mod.rs Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,3 @@ +pub mod outline; +pub mod wavefront_collapse; +pub mod maze; diff -r 2b4f361e3891 -r 629d5123a979 rust/mapgen/src/template/outline.rs --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/rust/mapgen/src/template/outline.rs Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,92 @@ +use integral_geometry::{Point, Rect, Size}; + +use landgen::outline_template_based::outline_template::OutlineTemplate; +use serde_derive::Deserialize; + +use std::collections::hash_map::HashMap; + +#[derive(Deserialize, Clone)] +pub struct PointDesc { + x: u32, + y: u32, +} + +#[derive(Deserialize, Clone)] +pub struct RectDesc { + x: u32, + y: u32, + w: u32, + h: u32, +} + +#[derive(Deserialize, Clone)] +pub struct TemplateDesc { + width: usize, + height: usize, + can_flip: bool, + can_invert: bool, + can_mirror: bool, + is_negative: bool, + put_girders: bool, + max_hedgehogs: u8, + outline_points: Vec>, + walls: Option>>, + fill_points: Vec, +} + +#[derive(Deserialize)] +pub struct TemplateTypeDesc { + pub indices: Vec, + pub force_invert: Option, +} + +#[derive(Deserialize)] +pub struct TemplateCollectionDesc { + pub templates: Vec, + pub template_types: HashMap, +} + +impl From for OutlineTemplate { + fn from(desc: TemplateDesc) -> Self { + OutlineTemplate { + islands: desc + .outline_points + .iter() + .map(|v| { + v.iter() + .map(|r| { + Rect::from_size( + Point::new(r.x as i32, r.y as i32), + Size::new(r.w as usize, r.h as usize), + ) + }) + .collect() + }) + .collect(), + walls: desc + .walls.unwrap_or_default() + .iter() + .map(|v| { + v.iter() + .map(|r| { + Rect::from_size( + Point::new(r.x as i32, r.y as i32), + Size::new(r.w as usize, r.h as usize), + ) + }) + .collect() + }) + .collect(), + fill_points: desc + .fill_points + .iter() + .map(|p| Point::new(p.x as i32, p.y as i32)) + .collect(), + size: Size::new(desc.width, desc.height), + can_flip: desc.can_flip, + can_invert: desc.can_invert, + can_mirror: desc.can_mirror, + is_negative: desc.is_negative, + } + } +} diff -r 2b4f361e3891 -r 629d5123a979 rust/mapgen/src/template/wavefront_collapse.rs --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/rust/mapgen/src/template/wavefront_collapse.rs Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,105 @@ +use integral_geometry::Size; + +use landgen::wavefront_collapse::generator::*; +use serde_derive::Deserialize; + +use std::collections::hash_map::HashMap; + +#[derive(Deserialize)] +#[serde(remote = "EdgeDescription")] +pub struct EdgeDesc { + pub name: String, + pub reversed: Option, + pub symmetrical: Option, +} + +#[derive(Deserialize)] +#[serde(remote = "EdgesDescription")] +pub struct EdgesDesc { + #[serde(with = "EdgeDesc")] + pub top: EdgeDescription, + #[serde(with = "EdgeDesc")] + pub right: EdgeDescription, + #[serde(with = "EdgeDesc")] + pub bottom: EdgeDescription, + #[serde(with = "EdgeDesc")] + pub left: EdgeDescription, +} + +#[derive(Deserialize)] +#[serde(remote = "TileDescription")] +pub struct TileDesc { + pub name: String, + #[serde(with = "EdgesDesc")] + pub edges: EdgesDescription, + pub is_negative: Option, + pub can_flip: Option, + pub can_mirror: Option, + pub can_rotate90: Option, + pub can_rotate180: Option, + pub can_rotate270: Option, +} + +#[derive(Deserialize)] +pub struct TileDescriptionHelper(#[serde(with = "TileDesc")] TileDescription); +#[derive(Deserialize)] +pub struct EdgeDescriptionHelper(#[serde(with = "EdgeDesc")] EdgeDescription); + +#[derive(Deserialize)] +pub struct NonStrictEdgesDesc { + pub top: Option, + pub right: Option, + pub bottom: Option, + pub left: Option, +} + +#[derive(Deserialize)] +pub struct TemplateDesc { + pub width: usize, + pub height: usize, + pub can_invert: bool, + pub is_negative: bool, + pub put_girders: bool, + pub max_hedgehogs: u8, + pub wrap: bool, + pub edges: Option, + pub tiles: Vec, +} + +#[derive(Deserialize)] +pub struct TemplateCollectionDesc { + pub templates: Vec, + pub template_types: HashMap>, +} + +impl From<&TemplateDesc> for TemplateDescription { + fn from(desc: &TemplateDesc) -> Self { + let [top, right, bottom, left] = if let Some(edges) = &desc.edges { + [ + edges.top.as_ref(), + edges.right.as_ref(), + edges.bottom.as_ref(), + edges.left.as_ref(), + ] + .map(|e| e.map(|EdgeDescriptionHelper(e)| e.clone())) + } else { + [None, None, None, None] + }; + + Self { + size: Size::new(desc.width, desc.height), + tiles: desc + .tiles + .iter() + .map(|TileDescriptionHelper(t)| t.clone()) + .collect(), + wrap: desc.wrap, + edges: NonStrictEdgesDescription { + top, + right, + bottom, + left, + }, + } + } +} diff -r 2b4f361e3891 -r 629d5123a979 rust/mapgen/src/theme.rs --- a/rust/mapgen/src/theme.rs Sat Sep 28 22:27:13 2024 +0200 +++ b/rust/mapgen/src/theme.rs Tue Dec 31 15:19:43 2024 +0100 @@ -48,7 +48,7 @@ pub fn to_transposed(&self) -> ThemeSprite { let size = self.size().transpose(); - let mut pixels = Vec2D::new(size, 0u32); + let mut pixels = Vec2D::new(&size, 0u32); for (y, row) in self.pixels.rows().enumerate() { for (x, v) in row.iter().enumerate() { pixels[x][y] = *v; @@ -238,7 +238,7 @@ } let size = Size::new(info.width as usize, info.height as usize); - let mut pixels: Vec2D = Vec2D::new(size, 0); + let mut pixels: Vec2D = Vec2D::new(&size, 0); reader.next_frame(slice_u32_to_u8_mut(pixels.as_mut_slice()))?; Ok(ThemeSprite { pixels }) diff -r 2b4f361e3891 -r 629d5123a979 rust/vec2d/src/lib.rs --- a/rust/vec2d/src/lib.rs Sat Sep 28 22:27:13 2024 +0200 +++ b/rust/vec2d/src/lib.rs Tue Dec 31 15:19:43 2024 +0100 @@ -1,9 +1,10 @@ +use integral_geometry::Size; use std::{ ops::{Index, IndexMut}, - slice::SliceIndex + slice::SliceIndex, }; -use integral_geometry::Size; +#[derive(Debug)] pub struct Vec2D { data: Vec, size: Size, @@ -33,7 +34,7 @@ } } -impl Vec2D { +impl Vec2D { #[inline] pub fn width(&self) -> usize { self.size.width @@ -51,8 +52,11 @@ } impl Vec2D { - pub fn new(size: Size, value: T) -> Self { - Self { size, data: vec![value; size.area()] } + pub fn new(size: &Size, value: T) -> Self { + Self { + size: *size, + data: vec![value; size.area()], + } } #[inline] @@ -67,21 +71,41 @@ #[inline] pub fn get(&self, row: usize, column: usize) -> Option<&>::Output> { - self.data.get(row * self.width() + column) + if row < self.height() && column < self.width() { + Some(unsafe { self.data.get_unchecked(row * self.width() + column) }) + } else { + None + } } #[inline] - pub fn get_mut(&mut self, row: usize, column: usize) -> Option<&mut >::Output> { - self.data.get_mut(row * self.size.width + column) + pub fn get_mut( + &mut self, + row: usize, + column: usize, + ) -> Option<&mut >::Output> { + if row < self.height() && column < self.width() { + Some(unsafe { self.data.get_unchecked_mut(row * self.size.width + column) }) + } else { + None + } } #[inline] - pub unsafe fn get_unchecked(&self, row: usize, column: usize) -> &>::Output { + pub unsafe fn get_unchecked( + &self, + row: usize, + column: usize, + ) -> &>::Output { self.data.get_unchecked(row * self.width() + column) } #[inline] - pub unsafe fn get_unchecked_mut(&mut self, row: usize, column: usize) -> &mut >::Output { + pub unsafe fn get_unchecked_mut( + &mut self, + row: usize, + column: usize, + ) -> &mut >::Output { self.data.get_unchecked_mut(row * self.size.width + column) } @@ -98,11 +122,8 @@ #[inline] pub unsafe fn as_bytes(&self) -> &[u8] { - use std::{ - slice, - mem - }; - + use std::{mem, slice}; + slice::from_raw_parts( self.data.as_ptr() as *const u8, self.data.len() * mem::size_of::(), @@ -122,6 +143,17 @@ } } +impl Vec2D { + pub fn from_iter>(iter: I, size: &Size) -> Option> { + let data: Vec = iter.into_iter().collect(); + if size.width * size.height == data.len() { + Some(Vec2D { data, size: *size }) + } else { + None + } + } +} + #[cfg(test)] mod tests { use super::*; diff -r 2b4f361e3891 -r 629d5123a979 share/hedgewars/Data/CMakeLists.txt --- a/share/hedgewars/Data/CMakeLists.txt Sat Sep 28 22:27:13 2024 +0200 +++ b/share/hedgewars/Data/CMakeLists.txt Tue Dec 31 15:19:43 2024 +0100 @@ -10,6 +10,7 @@ Scripts Sounds Themes + Tiles misc ) add_subdirectory(${dir}) @@ -18,3 +19,9 @@ if(${GL2}) add_subdirectory(Shaders) endif(${GL2}) + +install(FILES + map_templates.yaml + maze_templates.yaml + wfc_templates.yaml + DESTINATION ${SHAREPATH}Data) diff -r 2b4f361e3891 -r 629d5123a979 share/hedgewars/Data/Tiles/120_bar.png Binary file share/hedgewars/Data/Tiles/120_bar.png has changed diff -r 2b4f361e3891 -r 629d5123a979 share/hedgewars/Data/Tiles/120_corner.png Binary file share/hedgewars/Data/Tiles/120_corner.png has changed diff -r 2b4f361e3891 -r 629d5123a979 share/hedgewars/Data/Tiles/120_filled.png Binary file share/hedgewars/Data/Tiles/120_filled.png has changed diff -r 2b4f361e3891 -r 629d5123a979 share/hedgewars/Data/Tiles/120_two_corners.png Binary file share/hedgewars/Data/Tiles/120_two_corners.png has changed diff -r 2b4f361e3891 -r 629d5123a979 share/hedgewars/Data/Tiles/CMakeLists.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/share/hedgewars/Data/Tiles/CMakeLists.txt Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,5 @@ +file(GLOB tiles *.png) + +install(FILES + ${tiles} + DESTINATION ${SHAREPATH}Data/Tiles) diff -r 2b4f361e3891 -r 629d5123a979 share/hedgewars/Data/map_templates.yaml --- a/share/hedgewars/Data/map_templates.yaml Sat Sep 28 22:27:13 2024 +0200 +++ b/share/hedgewars/Data/map_templates.yaml Tue Dec 31 15:19:43 2024 +0100 @@ -32,6 +32,10 @@ - {x: 2018, y: 872, w: 276, h: 314} - {x: 2110, y: 1250, w: 130, h: 86} - {x: 2134, y: 1424, w: 1, h: 1} + walls: + - + - {x: 1570, y: 1424, w: 1, h: 1} + - {x: 1610, y: 104, w: 150, h: 10} fill_points: - {x: 1023, y: 0} @@ -193,7 +197,7 @@ - - {x: 674, y: 1424, w: 1, h: 1} - {x: 590, y: 1318, w: 168, h: 26} - - {x: 782, y: 976, w: 122, h: 314} + - {x: 632, y: 876, w: 122, h: 314} - {x: 968, y: 1144, w: 56, h: 180} - {x: 1078, y: 1256, w: 64, h: 56} - {x: 1140, y: 1050, w: 106, h: 220} @@ -203,9 +207,16 @@ - {x: 1350, y: 1152, w: 152, h: 146} - {x: 1572, y: 1174, w: 60, h: 152} - {x: 1684, y: 1122, w: 150, h: 138} - - {x: 1894, y: 764, w: 56, h: 582} + - {x: 1934, y: 764, w: 56, h: 582} - {x: 2020, y: 1174, w: 94, h: 232} - {x: 2012, y: 1424, w: 1, h: 1} + walls: + - + - {x: 990, y: 1070, w: 1, h: 1} + - {x: 200, y: 10, w: 350, h: 1} + - + - {x: 1600, y: 1100, w: 1, h: 1} + - {x: 2300, y: 10, w: 200, h: 1} fill_points: - {x: 1023, y: 0} @@ -226,16 +237,22 @@ - {x: 768, y: 1422, w: 2, h: 2} - {x: 666, y: 1240, w: 302, h: 110} - {x: 694, y: 912, w: 104, h: 290} - - {x: 970, y: 980, w: 364, h: 122} - - {x: 968, y: 840, w: 368, h: 100} + - {x: 970, y: 980, w: 164, h: 122} + - {x: 968, y: 840, w: 268, h: 100} - {x: 632, y: 660, w: 482, h: 130} - {x: 1178, y: 642, w: 62, h: 64} - {x: 1390, y: 554, w: 58, h: 246} - {x: 1600, y: 676, w: 590, h: 98} - - {x: 1488, y: 842, w: 214, h: 188} + - {x: 1488, y: 842, w: 114, h: 188} - {x: 1450, y: 1086, w: 406, h: 92} - {x: 1984, y: 902, w: 190, h: 412} - {x: 2046, y: 1420, w: 2, h: 2} + walls: + - + - {x: 1400, y: 822, w: 1, h: 1} + - {x: 1400, y: 1222, w: 1, h: 1} + - {x: 1900, y: 1240, w: 1, h: 100} + - {x: 1100, y: 1402, w: 1, h: 1} fill_points: - {x: 1023, y: 0} @@ -294,6 +311,10 @@ - {x: 1940, y: 988, w: 212, h: 50} - {x: 1864, y: 1146, w: 128, h: 146} - {x: 2030, y: 1424, w: 20, h: 1} + walls: + - + - {x: 1410, y: 924, w: 20, h: 1} + - {x: 1100, y: 2, w: 800, h: 1} fill_points: - {x: 1023, y: 0} @@ -363,15 +384,17 @@ - {x: 640, y: 1082, w: 140, h: 150} - {x: 714, y: 868, w: 352, h: 94} - {x: 1126, y: 646, w: 106, h: 282} - - {x: 1302, y: 790, w: 368, h: 142} + - {x: 1302, y: 790, w: 338, h: 142} - {x: 1358, y: 988, w: 116, h: 244} - {x: 1276, y: 1424, w: 14, h: 1} - - {x: 1464, y: 1424, w: 22, h: 1} - - {x: 1688, y: 1195, w: 120, h: 120} - - {x: 1858, y: 674, w: 354, h: 448} - - {x: 2088, y: 1195, w: 120, h: 120} + - {x: 1888, y: 674, w: 354, h: 448} - {x: 2182, y: 1424, w: 2, h: 1} + walls: + - + - {x: 1380, y: 1424, w: 1, h: 1} + - {x: 2250, y: 2, w: 1, h: 1} fill_points: - {x: 1023, y: 0} @@ -390,14 +413,18 @@ outline_points: - - {x: 674, y: 1424, w: 166, h: 1} - - {x: 730, y: 1262, w: 96, h: 92} - - {x: 892, y: 1090, w: 152, h: 250} - - {x: 1146, y: 1046, w: 36, h: 270} - - {x: 1338, y: 1026, w: 54, h: 224} - - {x: 1534, y: 1046, w: 44, h: 216} - - {x: 1692, y: 1030, w: 46, h: 300} - - {x: 1848, y: 1064, w: 158, h: 272} + - {x: 792, y: 990, w: 152, h: 250} + - {x: 1848, y: 964, w: 158, h: 272} - {x: 1984, y: 1424, w: 136, h: 1} + walls: + - + - {x: 980, y: 1284, w: 1, h: 50} + - {x: 1800, y: 1284, w: 1, h: 50} + - + - {x: 380, y: 610, w: 1, h: 200} + - {x: 2340, y: 610, w: 1, h: 200} + - {x: 2540, y: 1500, w: 1, h: 1} + - {x: 280, y: 1500, w: 1, h: 1} fill_points: - {x: 1023, y: 0} @@ -418,16 +445,23 @@ - {x: 760, y: 1424, w: 2, h: 2} - {x: 642, y: 1030, w: 46, h: 286} - {x: 854, y: 1072, w: 194, h: 56} - - {x: 654, y: 734, w: 534, h: 200} + - {x: 654, y: 734, w: 534, h: 100} - {x: 1270, y: 676, w: 58, h: 468} - {x: 1476, y: 672, w: 198, h: 112} - {x: 1400, y: 1424, w: 64, h: 2} - - {x: 1644, y: 1424, w: 64, h: 2} - {x: 1756, y: 894, w: 184, h: 94} - - {x: 2000, y: 814, w: 76, h: 358} - {x: 2148, y: 984, w: 108, h: 304} - {x: 2088, y: 1424, w: 176, h: 1} + walls: + - + - {x: 1560, y: 1424, w: 1, h: 1} + - {x: 1880, y: 1, w: 140, h: 1} + - + - {x: 860, y: 1424, w: 1, h: 1} + - {x: 1160, y: 1090, w: 1, h: 1} + - {x: 1330, y: 1424, w: 1, h: 1} fill_points: - {x: 1023, y: 0} @@ -446,20 +480,27 @@ outline_points: - - {x: 846, y: 1424, w: 140, h: 2} - - {x: 680, y: 1272, w: 196, h: 32} - {x: 654, y: 1080, w: 262, h: 134} - - {x: 1054, y: 1072, w: 220, h: 136} - - {x: 1008, y: 890, w: 268, h: 110} - - {x: 700, y: 762, w: 104, h: 200} + - {x: 1154, y: 1072, w: 160, h: 136} + - {x: 1148, y: 850, w: 168, h: 110} + - {x: 700, y: 762, w: 104, h: 60} - {x: 846, y: 624, w: 306, h: 58} - - {x: 1316, y: 588, w: 84, h: 206} - - {x: 1548, y: 574, w: 104, h: 220} - {x: 1826, y: 576, w: 120, h: 202} - {x: 1956, y: 818, w: 192, h: 68} - - {x: 1626, y: 948, w: 246, h: 88} - - {x: 1656, y: 1106, w: 194, h: 150} + - {x: 1606, y: 828, w: 126, h: 88} + - {x: 1606, y: 1106, w: 94, h: 150} - {x: 1968, y: 1106, w: 198, h: 152} - {x: 1844, y: 1424, w: 2, h: 2} + walls: + - + - {x: 0, y: 854, w: 1, h: 1} + - {x: 980, y: 985, w: 1, h: 1} + - + - {x: 1870, y: 1050, w: 1, h: 1} + - {x: 3000, y: 804, w: 1, h: 1} + - + - {x: 1420, y: 750, w: 90, h: 100} + - {x: 1450, y: 1424, w: 51, h: 1} fill_points: - {x: 1023, y: 0} @@ -509,27 +550,32 @@ - - {x: 702, y: 1424, w: 2, h: 2} - {x: 640, y: 1290, w: 44, h: 94} - - {x: 750, y: 1262, w: 44, h: 94} - {x: 860, y: 1306, w: 78, h: 70} - {x: 866, y: 1424, w: 2, h: 2} - - {x: 1204, y: 1424, w: 2, h: 2} - - {x: 1120, y: 1182, w: 108, h: 174} - - {x: 884, y: 1024, w: 314, h: 98} - - {x: 710, y: 882, w: 76, h: 230} - - {x: 834, y: 686, w: 220, h: 154} - - {x: 1240, y: 674, w: 56, h: 266} - - {x: 1424, y: 644, w: 78, h: 304} - - {x: 1648, y: 646, w: 116, h: 162} + - {x: 1120, y: 982, w: 108, h: 174} + - {x: 610, y: 842, w: 116, h: 230} + - {x: 1034, y: 686, w: 620, h: 154} - {x: 1980, y: 726, w: 190, h: 228} - - {x: 1760, y: 1004, w: 140, h: 84} - - {x: 1596, y: 1140, w: 242, h: 118} + - {x: 1536, y: 1040, w: 242, h: 118} - {x: 1616, y: 1424, w: 2, h: 2} - - {x: 1894, y: 1424, w: 2, h: 2} - {x: 1850, y: 1328, w: 88, h: 34} - {x: 1998, y: 1238, w: 96, h: 112} - {x: 2056, y: 1424, w: 2, h: 2} + walls: + - + - {x: 0, y: 1200, w: 1, h: 1} + - {x: 1040, y: 1200, w: 1, h: 1} + - {x: 1040, y: 1500, w: 1, h: 1} + - {x: 0, y: 1500, w: 2, h: 2} + - + - {x: 3100, y: 1120, w: 1, h: 1} + - {x: 1770, y: 1220, w: 1, h: 1} + - {x: 1770, y: 1500, w: 1, h: 1} + - {x: 3100, y: 1500, w: 2, h: 2} fill_points: - {x: 1023, y: 0} @@ -594,17 +640,18 @@ - - {x: 630, y: 1424, w: 2, h: 2} - {x: 566, y: 1256, w: 128, h: 118} - - {x: 752, y: 1256, w: 98, h: 114} - - {x: 748, y: 1074, w: 140, h: 138} - - {x: 956, y: 1072, w: 136, h: 142} - - {x: 1146, y: 1070, w: 114, h: 252} - - {x: 1324, y: 778, w: 120, h: 390} - - {x: 1522, y: 862, w: 114, h: 210} - - {x: 1724, y: 706, w: 130, h: 252} - - {x: 1936, y: 606, w: 278, h: 234} - - {x: 1924, y: 1044, w: 272, h: 52} - - {x: 1972, y: 1252, w: 180, h: 56} + - {x: 2036, y: 636, w: 78, h: 234} - {x: 1998, y: 1424, w: 42, h: 2} + walls: + - + - {x: 330, y: 1500, w: 2, h: 1} + - {x: 366, y: 1156, w: 64, h: 64} + - {x: 2086, y: 460, w: 200, h: 60} + - {x: 2100, y: 1500, w: 200, h: 1} + - + - {x: 830, y: 1424, w: 2, h: 1} + - {x: 1686, y: 1060, w: 200, h: 60} + - {x: 1650, y: 1424, w: 200, h: 1} fill_points: - {x: 1023, y: 0} @@ -898,33 +945,31 @@ - - {x: 474, y: 1424, w: 1, h: 1} - {x: 390, y: 1318, w: 168, h: 26} - - {x: 582, y: 976, w: 122, h: 314} - - {x: 768, y: 1144, w: 56, h: 180} - - {x: 878, y: 1256, w: 64, h: 56} - - {x: 940, y: 1050, w: 106, h: 220} + - {x: 940, y: 1080, w: 106, h: 220} - {x: 844, y: 896, w: 162, h: 140} - - {x: 696, y: 610, w: 886, h: 174} + - {x: 796, y: 610, w: 686, h: 174} - {x: 1134, y: 848, w: 296, h: 108} - {x: 1150, y: 1152, w: 152, h: 146} - - {x: 1372, y: 1174, w: 60, h: 152} - - {x: 1484, y: 1122, w: 150, h: 138} - {x: 1694, y: 764, w: 56, h: 582} - - {x: 1820, y: 1174, w: 94, h: 232} - - {x: 1812, y: 1424, w: 1, h: 1} + - {x: 1712, y: 1424, w: 1, h: 1} - - {x: 2110, y: 1424, w: 2, h: 2} - {x: 1992, y: 1030, w: 46, h: 286} - {x: 2204, y: 1072, w: 194, h: 56} - {x: 2004, y: 734, w: 534, h: 200} - - {x: 2620, y: 676, w: 58, h: 468} - - {x: 2826, y: 672, w: 198, h: 112} + - {x: 2626, y: 672, w: 198, h: 112} - {x: 2750, y: 1424, w: 64, h: 2} - - {x: 2994, y: 1424, w: 64, h: 2} - {x: 3106, y: 894, w: 184, h: 94} - - {x: 3350, y: 814, w: 76, h: 358} - {x: 3498, y: 984, w: 108, h: 304} - {x: 3438, y: 1424, w: 176, h: 1} + walls: + - + - {x: 1870, y: 1500, w: 1, h: 1} + - {x: 1780, y: 500, w: 168, h: 26} + - {x: 2880, y: 500, w: 265, h: 26} + - {x: 2880, y: 1500, w: 1, h: 1} fill_points: - {x: 2047, y: 0} @@ -984,23 +1029,26 @@ - - {x: 362, y: 1424, w: 400, h: 1} - {x: 426, y: 634, w: 142, h: 360} - - {x: 1136, y: 1140, w: 400, h: 200} - - {x: 1776, y: 576, w: 186, h: 550} - - {x: 1630, y: 1424, w: 454, h: 1} + - {x: 1206, y: 1140, w: 200, h: 200} + - {x: 1776, y: 576, w: 70, h: 550} + - {x: 1540, y: 1424, w: 120, h: 1} - - {x: 1938, y: 1424, w: 190, h: 1} - - {x: 1990, y: 1082, w: 140, h: 150} - {x: 2064, y: 868, w: 352, h: 94} - - {x: 2476, y: 646, w: 106, h: 282} - - {x: 2652, y: 790, w: 368, h: 142} - - {x: 2708, y: 988, w: 116, h: 244} + - {x: 2652, y: 790, w: 168, h: 142} - {x: 2626, y: 1424, w: 14, h: 1} - - {x: 2814, y: 1424, w: 22, h: 1} - - {x: 3038, y: 1195, w: 120, h: 120} - {x: 3208, y: 674, w: 354, h: 448} - - {x: 3438, y: 1195, w: 120, h: 120} - {x: 3532, y: 1424, w: 2, h: 1} + walls: + - + - {x: 560, y: 0, w: 260, h: 1} + - {x: 1250, y: 880, w: 40, h: 200} + - {x: 2000, y: 0, w: 260, h: 1} + - {x: 1880, y: 1500, w: 1, h: 1} + - {x: 2700, y: 1500, w: 1, h: 1} + - {x: 3050, y: 0, w: 480, h: 1} fill_points: - {x: 2047, y: 0} @@ -1019,56 +1067,44 @@ outline_points: - - {x: 564, y: 1424, w: 20, h: 1} - - {x: 490, y: 1260, w: 64, h: 62} - - {x: 686, y: 1150, w: 52, h: 146} - - {x: 456, y: 990, w: 116, h: 144} - - {x: 670, y: 868, w: 138, h: 168} - {x: 442, y: 642, w: 158, h: 162} - - {x: 708, y: 710, w: 198, h: 72} - {x: 970, y: 628, w: 118, h: 134} - - {x: 836, y: 1118, w: 142, h: 132} - - {x: 1168, y: 1100, w: 172, h: 58} - - {x: 1170, y: 1204, w: 172, h: 62} - - {x: 1432, y: 1104, w: 82, h: 226} + - {x: 836, y: 1118, w: 142, h: 32} - {x: 1556, y: 994, w: 64, h: 152} - {x: 1414, y: 734, w: 106, h: 152} - - {x: 1610, y: 660, w: 380, h: 82} - - {x: 1728, y: 822, w: 30, h: 118} - - {x: 1740, y: 988, w: 212, h: 50} - - {x: 1664, y: 1146, w: 128, h: 146} + - {x: 1610, y: 660, w: 260, h: 82} - {x: 1830, y: 1424, w: 20, h: 1} - - {x: 2140, y: 1424, w: 1, h: 1} - - {x: 2076, y: 1302, w: 44, h: 54} - {x: 2234, y: 1236, w: 58, h: 90} - - {x: 2066, y: 1134, w: 80, h: 80} - {x: 2046, y: 1004, w: 96, h: 108} - {x: 2226, y: 1046, w: 110, h: 112} - {x: 2034, y: 692, w: 118, h: 164} - - {x: 2228, y: 796, w: 130, h: 110} - - {x: 2316, y: 598, w: 344, h: 78} - - {x: 2488, y: 826, w: 50, h: 40} + - {x: 2316, y: 598, w: 264, h: 78} - {x: 2426, y: 960, w: 32, h: 148} - - {x: 2498, y: 1050, w: 160, h: 34} - {x: 2474, y: 1188, w: 36, h: 136} - {x: 2814, y: 1248, w: 48, h: 48} - {x: 2886, y: 1128, w: 64, h: 88} - - {x: 2758, y: 1060, w: 70, h: 74} - - {x: 2916, y: 996, w: 68, h: 70} - - {x: 2918, y: 884, w: 68, h: 82} - - {x: 2758, y: 724, w: 44, h: 140} + - {x: 2798, y: 724, w: 44, h: 140} - {x: 3072, y: 706, w: 52, h: 66} - - {x: 3054, y: 902, w: 58, h: 66} - {x: 3034, y: 1160, w: 76, h: 112} - - {x: 3180, y: 1162, w: 124, h: 64} - - {x: 3272, y: 872, w: 54, h: 134} - - {x: 3210, y: 596, w: 246, h: 62} + - {x: 3290, y: 1162, w: 40, h: 64} + - {x: 3310, y: 596, w: 46, h: 62} - {x: 3506, y: 554, w: 38, h: 238} - - {x: 3612, y: 748, w: 28, h: 28} - - {x: 3492, y: 924, w: 144, h: 94} - - {x: 3432, y: 1078, w: 248, h: 20} - - {x: 3432, y: 1202, w: 238, h: 16} - {x: 3480, y: 1424, w: 1, h: 1} + walls: + - + - {x: 1264, y: 1024, w: 20, h: 1} + - {x: 1070, y: 0, w: 400, h: 1} + - + - {x: 2670, y: 1170, w: 1, h: 1} + - {x: 2610, y: 0, w: 200, h: 1} + - + - {x: 3210, y: 1100, w: 1, h: 1} + - {x: 3100, y: 0, w: 210, h: 1} + - + - {x: 710, y: 1250, w: 1, h: 120} + - {x: 1630, y: 1200, w: 1, h: 170} fill_points: - {x: 2047, y: 0} @@ -1089,20 +1125,16 @@ - {x: 610, y: 1424, w: 1, h: 1} - {x: 360, y: 1160, w: 130, h: 170} - {x: 542, y: 1106, w: 316, h: 150} - - {x: 438, y: 786, w: 270, h: 180} - {x: 446, y: 576, w: 242, h: 156} - - {x: 752, y: 528, w: 610, h: 300} - - {x: 950, y: 868, w: 352, h: 324} - - {x: 850, y: 1424, w: 500, h: 1} - - {x: 1450, y: 1500, w: 1, h: 1} + - {x: 752, y: 528, w: 350, h: 300} + - {x: 950, y: 1424, w: 300, h: 1} + - - {x: 1690, y: 1424, w: 1, h: 1} - {x: 1652, y: 1304, w: 74, h: 12} - - {x: 1448, y: 975, w: 68, h: 425} + - {x: 1448, y: 975, w: 68, h: 385} - {x: 1626, y: 992, w: 140, h: 142} - {x: 1510, y: 592, w: 150, h: 350} - {x: 1788, y: 594, w: 148, h: 242} - - {x: 1818, y: 872, w: 276, h: 314} - - {x: 1910, y: 1250, w: 130, h: 86} - {x: 1934, y: 1424, w: 1, h: 1} - - {x: 2202, y: 1424, w: 2, h: 2} @@ -1113,21 +1145,23 @@ - - {x: 2704, y: 1424, w: 2, h: 2} - {x: 2620, y: 1182, w: 108, h: 174} - - {x: 2384, y: 1024, w: 314, h: 98} - - {x: 2210, y: 882, w: 76, h: 230} + - {x: 2210, y: 902, w: 76, h: 230} - {x: 2334, y: 686, w: 220, h: 154} - - {x: 2740, y: 674, w: 56, h: 266} - - {x: 2924, y: 644, w: 78, h: 304} - - {x: 3148, y: 646, w: 116, h: 162} - - {x: 3480, y: 726, w: 190, h: 228} - - {x: 3260, y: 1004, w: 140, h: 84} - - {x: 3096, y: 1140, w: 242, h: 118} + - {x: 3480, y: 680, w: 190, h: 228} + - {x: 3096, y: 1010, w: 242, h: 118} - {x: 3116, y: 1424, w: 2, h: 2} - - {x: 3394, y: 1424, w: 2, h: 2} - {x: 3350, y: 1328, w: 88, h: 34} - {x: 3498, y: 1238, w: 96, h: 112} - {x: 3556, y: 1424, w: 2, h: 2} + walls: + - + - {x: 1300, y: 1500, w: 1, h: 1} + - {x: 1100, y: 960, w: 280, h: 40} + - {x: 1200, y: 1, w: 280, h: 1} + - {x: 2000, y: 400, w: 250, h: 1} + - {x: 2020, y: 1500, w: 20, h: 1} fill_points: - {x: 2047, y: 0} @@ -1157,19 +1191,14 @@ - {x: 1212, y: 990, w: 188, h: 298} - {x: 1440, y: 1068, w: 136, h: 172} - {x: 1470, y: 594, w: 120, h: 392} - - {x: 1714, y: 594, w: 364, h: 362} - - {x: 1650, y: 1052, w: 315, h: 232} + - {x: 1714, y: 594, w: 220, h: 362} - {x: 1660, y: 1424, w: 25, h: 1} - - {x: 1986, y: 1424, w: 2, h: 2} - - {x: 1944, y: 1286, w: 84, h: 54} - {x: 1912, y: 1086, w: 150, h: 166} - {x: 2378, y: 1240, w: 186, h: 98} - - {x: 2444, y: 1004, w: 124, h: 58} - {x: 2320, y: 582, w: 112, h: 194} - - {x: 2688, y: 660, w: 92, h: 132} - {x: 3010, y: 574, w: 154, h: 196} - - {x: 2860, y: 974, w: 118, h: 64} - {x: 2752, y: 1222, w: 328, h: 92} - {x: 3296, y: 1030, w: 242, h: 222} - {x: 3298, y: 1316, w: 254, h: 50} @@ -1192,36 +1221,24 @@ outline_points: - - {x: 554, y: 1424, w: 1, h: 1} - - {x: 432, y: 1326, w: 226, h: 60} - - {x: 320, y: 1246, w: 298, h: 62} - - {x: 480, y: 1104, w: 210, h: 102} - {x: 408, y: 822, w: 192, h: 248} - - {x: 492, y: 560, w: 206, h: 240} - {x: 726, y: 572, w: 92, h: 334} - - {x: 662, y: 928, w: 226, h: 126} - {x: 756, y: 1078, w: 268, h: 156} - {x: 922, y: 564, w: 138, h: 500} - {x: 1090, y: 556, w: 94, h: 352} - - {x: 1098, y: 962, w: 170, h: 264} - - {x: 1292, y: 784, w: 84, h: 446} + - {x: 1198, y: 962, w: 170, h: 264} - {x: 1406, y: 600, w: 158, h: 278} - - {x: 1500, y: 890, w: 104, h: 336} - {x: 1616, y: 946, w: 90, h: 398} - - {x: 1746, y: 592, w: 134, h: 532} - - {x: 1902, y: 646, w: 156, h: 258} - - {x: 1900, y: 948, w: 132, h: 340} - - {x: 1734, y: 1298, w: 252, h: 82} + - {x: 1746, y: 592, w: 94, h: 532} + - {x: 1734, y: 1348, w: 252, h: 32} - {x: 1804, y: 1424, w: 1, h: 1} - - {x: 2096, y: 1424, w: 140, h: 2} - - {x: 1930, y: 1272, w: 196, h: 32} - - {x: 1904, y: 1080, w: 262, h: 134} - - {x: 2304, y: 1072, w: 220, h: 136} + - {x: 1944, y: 1080, w: 262, h: 134} + - {x: 2344, y: 1072, w: 200, h: 136} - {x: 2258, y: 890, w: 268, h: 110} - {x: 1950, y: 762, w: 104, h: 200} - {x: 2096, y: 624, w: 306, h: 58} - - {x: 2566, y: 588, w: 84, h: 206} - - {x: 2798, y: 574, w: 104, h: 220} - {x: 3076, y: 576, w: 120, h: 202} - {x: 3206, y: 818, w: 192, h: 68} - {x: 2876, y: 948, w: 246, h: 88} @@ -1250,29 +1267,24 @@ - {x: 698, y: 1110, w: 308, h: 60} - {x: 928, y: 1252, w: 434, h: 40} - {x: 1374, y: 1112, w: 332, h: 40} - - {x: 1602, y: 1238, w: 226, h: 36} - {x: 1730, y: 1424, w: 1, h: 1} - - - {x: 1860, y: 898, w: 111, h: 111} - - {x: 1470, y: 876, w: 34, h: 102} - - {x: 882, y: 814, w: 284, h: 132} + - {x: 1660, y: 898, w: 111, h: 111} - {x: 430, y: 728, w: 126, h: 168} - {x: 610, y: 574, w: 114, h: 100} - - {x: 990, y: 572, w: 352, h: 120} - - {x: 1474, y: 528, w: 60, h: 240} - - {x: 1634, y: 622, w: 254, h: 116} + - {x: 1534, y: 622, w: 254, h: 116} - - {x: 1960, y: 1424, w: 2, h: 2} - {x: 1842, y: 1030, w: 46, h: 286} - {x: 2054, y: 1072, w: 194, h: 56} - {x: 1854, y: 734, w: 534, h: 200} - - {x: 2470, y: 676, w: 58, h: 468} + - {x: 2470, y: 676, w: 58, h: 640} - {x: 2676, y: 672, w: 198, h: 112} - {x: 2600, y: 1424, w: 64, h: 2} - - {x: 2844, y: 1424, w: 64, h: 2} - {x: 2956, y: 894, w: 184, h: 94} - - {x: 3200, y: 814, w: 76, h: 358} + - {x: 3200, y: 814, w: 76, h: 510} - {x: 3348, y: 984, w: 108, h: 304} - {x: 3288, y: 1424, w: 176, h: 1} fill_points: @@ -1344,22 +1356,16 @@ - {x: 474, y: 1424, w: 1, h: 1} - {x: 390, y: 1318, w: 168, h: 26} - {x: 582, y: 976, w: 122, h: 314} - - {x: 768, y: 1144, w: 56, h: 180} - {x: 878, y: 1256, w: 64, h: 56} - - {x: 940, y: 1050, w: 106, h: 220} - {x: 844, y: 896, w: 162, h: 140} - {x: 696, y: 610, w: 886, h: 174} - {x: 1134, y: 848, w: 296, h: 108} - {x: 1150, y: 1152, w: 152, h: 146} - - {x: 1372, y: 1174, w: 60, h: 152} - - {x: 1484, y: 1122, w: 150, h: 138} - {x: 1694, y: 764, w: 56, h: 582} - - {x: 1820, y: 1174, w: 94, h: 232} - {x: 1812, y: 1424, w: 1, h: 1} - - {x: 2088, y: 1424, w: 190, h: 1} - {x: 2140, y: 1082, w: 140, h: 150} - - {x: 2214, y: 868, w: 352, h: 94} - {x: 2626, y: 646, w: 106, h: 282} - {x: 2802, y: 790, w: 368, h: 142} - {x: 2858, y: 988, w: 116, h: 244} @@ -1451,29 +1457,26 @@ - - {x: 362, y: 1424, w: 400, h: 1} - {x: 426, y: 634, w: 142, h: 360} - - {x: 1136, y: 1140, w: 400, h: 200} - - {x: 1776, y: 576, w: 186, h: 550} - - {x: 1630, y: 1424, w: 454, h: 1} + - {x: 1036, y: 1140, w: 400, h: 200} + - {x: 1576, y: 576, w: 186, h: 550} + - {x: 1430, y: 1424, w: 454, h: 1} - - {x: 1964, y: 1424, w: 20, h: 1} - - {x: 1890, y: 1260, w: 64, h: 62} - - {x: 2086, y: 1150, w: 52, h: 146} - - {x: 1856, y: 990, w: 116, h: 144} - - {x: 2070, y: 868, w: 138, h: 168} - {x: 1842, y: 642, w: 158, h: 162} - - {x: 2108, y: 710, w: 198, h: 72} - {x: 2370, y: 628, w: 118, h: 134} - {x: 2236, y: 1118, w: 142, h: 132} - - {x: 2568, y: 1100, w: 172, h: 58} - - {x: 2570, y: 1204, w: 172, h: 62} - {x: 2832, y: 1104, w: 82, h: 226} - - {x: 2956, y: 994, w: 64, h: 152} - {x: 2814, y: 734, w: 106, h: 152} - {x: 3010, y: 660, w: 380, h: 82} - - {x: 3128, y: 822, w: 30, h: 118} - - {x: 3140, y: 988, w: 212, h: 50} - - {x: 3064, y: 1146, w: 128, h: 146} - {x: 3230, y: 1424, w: 20, h: 1} + walls: + - + - {x: 400, y: 1, w: 300, h: 1} + - {x: 1500, y: 1, w: 300, h: 1} + - {x: 1200, y: 1100, w: 1, h: 1} + - + - {x: 2500, y: 1, w: 400, h: 1} + - {x: 2580, y: 1100, w: 1, h: 1} fill_points: - {x: 2047, y: 0} @@ -1556,24 +1559,10 @@ max_hedgehogs: 32 outline_points: - - - {x: 324, y: 756, w: 196, h: 204} - - {x: 224, y: 596, w: 404, h: 60} - - {x: 240, y: 268, w: 464, h: 152} - - {x: 876, y: 236, w: 168, h: 348} - - {x: 1204, y: 56, w: 148, h: 700} - - {x: 1516, y: 52, w: 192, h: 664} - - {x: 1808, y: 60, w: 328, h: 496} - - {x: 2292, y: 92, w: 184, h: 492} - - {x: 2664, y: 216, w: 196, h: 340} - - {x: 3004, y: 108, w: 176, h: 480} - - {x: 3260, y: 368, w: 120, h: 348} - - {x: 3476, y: 460, w: 208, h: 448} - - {x: 3268, y: 906, w: 192, h: 96} - - {x: 2876, y: 664, w: 204, h: 310} - - {x: 2240, y: 748, w: 344, h: 224} - - {x: 1584, y: 796, w: 440, h: 250} - - {x: 892, y: 852, w: 324, h: 184} - - {x: 576, y: 976, w: 16, h: 28} + - {x: 324, y: 876, w: 86, h: 106} + - {x: 240, y: 248, w: 264, h: 152} + - {x: 3476, y: 210, w: 64, h: 248} + - {x: 3468, y: 866, w: 192, h: 96} fill_points: - {x: 2047, y: 0} @@ -1591,23 +1580,15 @@ max_hedgehogs: 48 outline_points: - - - {x: 700, y: 2100, w: 120, h: 175} - - {x: 800, y: 1200, w: 120, h: 175} + - {x: 700, y: 2100, w: 120, h: 1} - {x: 900, y: 400, w: 120, h: 150} - - {x: 1100, y: 600, w: 120, h: 150} - - {x: 1300, y: 900, w: 120, h: 150} - - {x: 1000, y: 1000, w: 120, h: 150} - - {x: 1700, y: 1850, w: 120, h: 175} - - {x: 2048, y: 2100, w: 120, h: 175} + - {x: 1100, y: 1600, w: 120, h: 150} + - {x: 1930, y: 2100, w: 120, h: 1} - - - {x: 2048, y: 2100, w: 120, h: 150} - - {x: 2400, y: 1850, w: 120, h: 150} - - {x: 2600, y: 1000, w: 120, h: 175} - - {x: 2800, y: 900, w: 120, h: 150} - - {x: 3000, y: 600, w: 120, h: 150} + - {x: 2048, y: 2100, w: 120, h: 1} + - {x: 2600, y: 1600, w: 120, h: 175} - {x: 3200, y: 400, w: 120, h: 150} - - {x: 3300, y: 1200, w: 120, h: 150} - - {x: 3400, y: 2100, w: 120, h: 175} + - {x: 3400, y: 2100, w: 120, h: 1} - - {x: 1450, y: 700, w: 125, h: 125} - {x: 1850, y: 500, w: 125, h: 125} @@ -1615,9 +1596,16 @@ - {x: 2500, y: 700, w: 125, h: 125} - - {x: 1550, y: 1500, w: 125, h: 125} - - {x: 1830, y: 1150, w: 125, h: 125} - - {x: 2260, y: 1000, w: 125, h: 125} + - {x: 1630, y: 1150, w: 125, h: 125} + - {x: 2260, y: 1200, w: 125, h: 125} - {x: 2250, y: 1400, w: 125, h: 125} + walls: + - + - {x: 1390, y: 1500, w: 42, h: 175} + - {x: 1200, y: 270, w: 42, h: 175} + - {x: 2900, y: 260, w: 42, h: 175} + - {x: 2450, y: 1550, w: 90, h: 90} + - {x: 2000, y: 1950, w: 90, h: 90} fill_points: - {x: 2047, y: 0} @@ -1636,20 +1624,19 @@ outline_points: - - {x: 100, y: 2100, w: 1, h: 1} - - {x: 100, y: 1600, w: 250, h: 500} - - {x: 400, y: 600, w: 250, h: 1500} - - {x: 700, y: 1600, w: 250, h: 600} - - {x: 1000, y: 1800, w: 250, h: 300} - - {x: 1300, y: 500, w: 250, h: 1600} - - {x: 1600, y: 1700, w: 150, h: 400} - - {x: 1800, y: 1600, w: 150, h: 500} - - {x: 2000, y: 1400, w: 150, h: 700} - - {x: 2200, y: 300, w: 250, h: 1800} - - {x: 2500, y: 1500, w: 250, h: 600} - - {x: 2800, y: 1900, w: 250, h: 200} - - {x: 3100, y: 1600, w: 250, h: 500} - - {x: 3400, y: 600, w: 250, h: 1500} - - {x: 3700, y: 1800, w: 150, h: 300} + - {x: 100, y: 1500, w: 250, h: 500} + - {x: 400, y: 500, w: 250, h: 1500} + - {x: 700, y: 1400, w: 250, h: 600} + - {x: 1000, y: 1700, w: 250, h: 300} + - {x: 1300, y: 400, w: 250, h: 1600} + - {x: 1600, y: 1600, w: 150, h: 400} + - {x: 1800, y: 1500, w: 150, h: 500} + - {x: 2000, y: 1300, w: 150, h: 700} + - {x: 2200, y: 200, w: 250, h: 1800} + - {x: 2500, y: 1400, w: 250, h: 600} + - {x: 2800, y: 1800, w: 250, h: 200} + - {x: 3100, y: 1500, w: 250, h: 500} + - {x: 3400, y: 500, w: 250, h: 1500} - {x: 3700, y: 2100, w: 1, h: 1} fill_points: - {x: 2047, y: 0} @@ -2053,7 +2040,7 @@ - {x: 3925, y: 2098, w: 75, h: 50} - {x: 4050, y: 2173, w: 50, h: 75} fill_points: - - {x: 4095, y: 0} + - {x: 4094, y: 0} @@ -2086,9 +2073,9 @@ fill_points: - {x: 1, y: 90} - {x: 1, y: 500} - - {x: 4095, y: 500} + - {x: 4094, y: 500} - {x: 1, y: 1200} - - {x: 4095, y: 1200} + - {x: 4094, y: 1200} - {x: 1, y: 2010} @@ -2158,9 +2145,369 @@ - {x: 1023, y: 0} + + # 47 + - + width: 3072 + height: 1424 + can_flip: false + can_invert: false + can_mirror: true + is_negative: false + put_girders: true + max_hedgehogs: 48 + outline_points: + - + - {x: 250, y: 1424, w: 1, h: 1} + - {x: 350, y: 1100, w: 800, h: 40} + - {x: 1250, y: 1424, w: 1, h: 5} + - + - {x: 1800, y: 1424, w: 1, h: 1} + - {x: 1900, y: 1100, w: 800, h: 40} + - {x: 2800, y: 1424, w: 1, h: 5} + - + - {x: 250, y: 624, w: 1, h: 1} + - {x: 350, y: 800, w: 800, h: 40} + - {x: 1250, y: 624, w: 1, h: 5} + - + - {x: 1800, y: 624, w: 1, h: 1} + - {x: 1900, y: 800, w: 800, h: 40} + - {x: 2800, y: 624, w: 1, h: 5} + walls: + - + - {x: 1386, y: 0, w: 300, h: 1} + - {x: 1386, y: 1424, w: 300, h: 1} + - + - {x: 0, y: 875, w: 1, h: 150} + - {x: 3072, y: 875, w: 1, h: 150} + fill_points: + - {x: 1386, y: 0} + + + + # 48 + - + width: 4096 + height: 1024 + can_flip: false + can_invert: false + can_mirror: false + is_negative: false + put_girders: false + max_hedgehogs: 48 + outline_points: + - + - {x: 273, y: 1024, w: 1, h: 1} + - {x: 683, y: 32, w: 1, h: 32} + - {x: 1092, y: 1024, w: 1, h: 1} + - + - {x: 1638, y: 1024, w: 1, h: 1} + - {x: 2048, y: 32, w: 1, h: 32} + - {x: 2458, y: 1024, w: 1, h: 1} + - + - {x: 3004, y: 1024, w: 1, h: 1} + - {x: 3413, y: 32, w: 1, h: 32} + - {x: 3823, y: 1024, w: 1, h: 1} + walls: + - + - {x: 1365, y: 0, w: 1, h: 1} + - {x: 1365, y: 1024, w: 1, h: 1} + - + - {x: 2731, y: 0, w: 1, h: 1} + - {x: 2731, y: 1024, w: 1, h: 1} + fill_points: + - {x: 1, y: 0} + + + + # 49 + - + width: 4096 + height: 1024 + can_flip: false + can_invert: false + can_mirror: false + is_negative: false + put_girders: false + max_hedgehogs: 48 + outline_points: + - + - {x: 205, y: 1024, w: 1, h: 1} + - {x: 512, y: 32, w: 1, h: 32} + - {x: 819, y: 1024, w: 1, h: 1} + - + - {x: 1229, y: 1024, w: 1, h: 1} + - {x: 1536, y: 32, w: 1, h: 32} + - {x: 1843, y: 1024, w: 1, h: 1} + - + - {x: 2253, y: 1024, w: 1, h: 1} + - {x: 2560, y: 32, w: 1, h: 32} + - {x: 2867, y: 1024, w: 1, h: 1} + - + - {x: 3277, y: 1024, w: 1, h: 1} + - {x: 3584, y: 32, w: 1, h: 32} + - {x: 3891, y: 1024, w: 1, h: 1} + walls: + - + - {x: 1024, y: 0, w: 1, h: 1} + - {x: 1024, y: 1024, w: 1, h: 1} + - + - {x: 2048, y: 0, w: 1, h: 1} + - {x: 2048, y: 1024, w: 1, h: 1} + - + - {x: 3072, y: 0, w: 1, h: 1} + - {x: 3072, y: 1024, w: 1, h: 1} + fill_points: + - {x: 1, y: 0} + + + + # 50 + - + width: 4096 + height: 1024 + can_flip: false + can_invert: false + can_mirror: false + is_negative: false + put_girders: false + max_hedgehogs: 48 + outline_points: + - + - {x: 164, y: 1024, w: 1, h: 1} + - {x: 410, y: 32, w: 1, h: 32} + - {x: 655, y: 1024, w: 1, h: 1} + - + - {x: 983, y: 1024, w: 1, h: 1} + - {x: 1229, y: 32, w: 1, h: 32} + - {x: 1475, y: 1024, w: 1, h: 1} + - + - {x: 1802, y: 1024, w: 1, h: 1} + - {x: 2048, y: 32, w: 1, h: 32} + - {x: 2294, y: 1024, w: 1, h: 1} + - + - {x: 2621, y: 1024, w: 1, h: 1} + - {x: 2867, y: 32, w: 1, h: 32} + - {x: 3113, y: 1024, w: 1, h: 1} + - + - {x: 3441, y: 1024, w: 1, h: 1} + - {x: 3686, y: 32, w: 1, h: 32} + - {x: 3932, y: 1024, w: 1, h: 1} + walls: + - + - {x: 819, y: 0, w: 1, h: 1} + - {x: 819, y: 1024, w: 1, h: 1} + - + - {x: 1638, y: 0, w: 1, h: 1} + - {x: 1638, y: 1024, w: 1, h: 1} + - + - {x: 2458, y: 0, w: 1, h: 1} + - {x: 2458, y: 1024, w: 1, h: 1} + - + - {x: 3277, y: 0, w: 1, h: 1} + - {x: 3277, y: 1024, w: 1, h: 1} + fill_points: + - {x: 1, y: 0} + + + + # 51 + - + width: 4096 + height: 1024 + can_flip: false + can_invert: false + can_mirror: false + is_negative: false + put_girders: false + max_hedgehogs: 48 + outline_points: + - + - {x: 137, y: 1024, w: 1, h: 1} + - {x: 341, y: 32, w: 1, h: 32} + - {x: 546, y: 1024, w: 1, h: 1} + - + - {x: 819, y: 1024, w: 1, h: 1} + - {x: 1024, y: 32, w: 1, h: 32} + - {x: 1229, y: 1024, w: 1, h: 1} + - + - {x: 1502, y: 1024, w: 1, h: 1} + - {x: 1707, y: 32, w: 1, h: 32} + - {x: 1911, y: 1024, w: 1, h: 1} + - + - {x: 2185, y: 1024, w: 1, h: 1} + - {x: 2389, y: 32, w: 1, h: 32} + - {x: 2594, y: 1024, w: 1, h: 1} + - + - {x: 2867, y: 1024, w: 1, h: 1} + - {x: 3072, y: 32, w: 1, h: 32} + - {x: 3277, y: 1024, w: 1, h: 1} + - + - {x: 3550, y: 1024, w: 1, h: 1} + - {x: 3755, y: 32, w: 1, h: 32} + - {x: 3959, y: 1024, w: 1, h: 1} + walls: + - + - {x: 683, y: 0, w: 1, h: 1} + - {x: 683, y: 1024, w: 1, h: 1} + - + - {x: 1365, y: 0, w: 1, h: 1} + - {x: 1365, y: 1024, w: 1, h: 1} + - + - {x: 2048, y: 0, w: 1, h: 1} + - {x: 2048, y: 1024, w: 1, h: 1} + - + - {x: 2731, y: 0, w: 1, h: 1} + - {x: 2731, y: 1024, w: 1, h: 1} + - + - {x: 3413, y: 0, w: 1, h: 1} + - {x: 3413, y: 1024, w: 1, h: 1} + fill_points: + - {x: 1, y: 0} + + + + # 52 + - + width: 4096 + height: 1024 + can_flip: false + can_invert: false + can_mirror: false + is_negative: false + put_girders: false + max_hedgehogs: 48 + outline_points: + - + - {x: 117, y: 1024, w: 1, h: 1} + - {x: 293, y: 32, w: 1, h: 32} + - {x: 468, y: 1024, w: 1, h: 1} + - + - {x: 702, y: 1024, w: 1, h: 1} + - {x: 878, y: 32, w: 1, h: 32} + - {x: 1053, y: 1024, w: 1, h: 1} + - + - {x: 1287, y: 1024, w: 1, h: 1} + - {x: 1463, y: 32, w: 1, h: 32} + - {x: 1638, y: 1024, w: 1, h: 1} + - + - {x: 1872, y: 1024, w: 1, h: 1} + - {x: 2048, y: 32, w: 1, h: 32} + - {x: 2224, y: 1024, w: 1, h: 1} + - + - {x: 2458, y: 1024, w: 1, h: 1} + - {x: 2633, y: 32, w: 1, h: 32} + - {x: 2809, y: 1024, w: 1, h: 1} + - + - {x: 3043, y: 1024, w: 1, h: 1} + - {x: 3218, y: 32, w: 1, h: 32} + - {x: 3394, y: 1024, w: 1, h: 1} + - + - {x: 3628, y: 1024, w: 1, h: 1} + - {x: 3803, y: 32, w: 1, h: 32} + - {x: 3979, y: 1024, w: 1, h: 1} + walls: + - + - {x: 585, y: 0, w: 1, h: 1} + - {x: 585, y: 1024, w: 1, h: 1} + - + - {x: 1170, y: 0, w: 1, h: 1} + - {x: 1170, y: 1024, w: 1, h: 1} + - + - {x: 1755, y: 0, w: 1, h: 1} + - {x: 1755, y: 1024, w: 1, h: 1} + - + - {x: 2341, y: 0, w: 1, h: 1} + - {x: 2341, y: 1024, w: 1, h: 1} + - + - {x: 2926, y: 0, w: 1, h: 1} + - {x: 2926, y: 1024, w: 1, h: 1} + - + - {x: 3511, y: 0, w: 1, h: 1} + - {x: 3511, y: 1024, w: 1, h: 1} + fill_points: + - {x: 1, y: 0} + + + + # 53 + - + width: 4096 + height: 1024 + can_flip: false + can_invert: false + can_mirror: false + is_negative: false + put_girders: false + max_hedgehogs: 48 + outline_points: + - + - {x: 102, y: 1024, w: 1, h: 1} + - {x: 256, y: 32, w: 1, h: 32} + - {x: 410, y: 1024, w: 1, h: 1} + - + - {x: 614, y: 1024, w: 1, h: 1} + - {x: 768, y: 32, w: 1, h: 32} + - {x: 922, y: 1024, w: 1, h: 1} + - + - {x: 1126, y: 1024, w: 1, h: 1} + - {x: 1280, y: 32, w: 1, h: 32} + - {x: 1434, y: 1024, w: 1, h: 1} + - + - {x: 1638, y: 1024, w: 1, h: 1} + - {x: 1792, y: 32, w: 1, h: 32} + - {x: 1946, y: 1024, w: 1, h: 1} + - + - {x: 2150, y: 1024, w: 1, h: 1} + - {x: 2304, y: 32, w: 1, h: 32} + - {x: 2458, y: 1024, w: 1, h: 1} + - + - {x: 2662, y: 1024, w: 1, h: 1} + - {x: 2816, y: 32, w: 1, h: 32} + - {x: 2970, y: 1024, w: 1, h: 1} + - + - {x: 3174, y: 1024, w: 1, h: 1} + - {x: 3328, y: 32, w: 1, h: 32} + - {x: 3482, y: 1024, w: 1, h: 1} + - + - {x: 3686, y: 1024, w: 1, h: 1} + - {x: 3840, y: 32, w: 1, h: 32} + - {x: 3994, y: 1024, w: 1, h: 1} + walls: + - + - {x: 512, y: 0, w: 1, h: 1} + - {x: 512, y: 1024, w: 1, h: 1} + - + - {x: 1024, y: 0, w: 1, h: 1} + - {x: 1024, y: 1024, w: 1, h: 1} + - + - {x: 1536, y: 0, w: 1, h: 1} + - {x: 1536, y: 1024, w: 1, h: 1} + - + - {x: 2048, y: 0, w: 1, h: 1} + - {x: 2048, y: 1024, w: 1, h: 1} + - + - {x: 2560, y: 0, w: 1, h: 1} + - {x: 2560, y: 1024, w: 1, h: 1} + - + - {x: 3072, y: 0, w: 1, h: 1} + - {x: 3072, y: 1024, w: 1, h: 1} + - + - {x: 3584, y: 0, w: 1, h: 1} + - {x: 3584, y: 1024, w: 1, h: 1} + fill_points: + - {x: 1, y: 0} + + template_types: - small: [39, 40, 42] - medium: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 46] - large: [18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 37, 38, 43] - cavern: [36, 2, 3, 21, 29, 45] - wacky: [37, 38, 41, 43, 44] + small: + indices: [39, 40, 42] + medium: + indices: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 46, 47] + large: + indices: [18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 37, 38, 43, 48, 49, 50, 51, 52, 53] + cavern: + indices: [36, 2, 3, 21, 29, 45] + force_invert: true + wacky: + indices: [37, 38, 41, 43, 44] diff -r 2b4f361e3891 -r 629d5123a979 share/hedgewars/Data/maze_templates.yaml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/share/hedgewars/Data/maze_templates.yaml Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,131 @@ +# Templates for maze map generator in hedgewars + +templates: + # 00 + - + width: 2048 + height: 1024 + max_hedgehogs: 64 + cell_size: 100 + distortion_limiting_factor: 140 + braidness: 12 + invert: false + + # 01 + - + width: 1536 + height: 1024 + max_hedgehogs: 64 + cell_size: 64 + distortion_limiting_factor: 160 + braidness: 8 + invert: false + + # 02 + - + width: 2048 + height: 1024 + max_hedgehogs: 64 + cell_size: 140 + distortion_limiting_factor: 140 + braidness: 12 + invert: false + + # 03 + - + width: 4096 + height: 2048 + max_hedgehogs: 64 + cell_size: 160 + distortion_limiting_factor: 180 + braidness: 16 + invert: false + + # 04 + - + width: 4096 + height: 2048 + max_hedgehogs: 64 + cell_size: 240 + distortion_limiting_factor: 220 + braidness: 20 + invert: false + + # 05 + - + width: 4096 + height: 2048 + max_hedgehogs: 64 + cell_size: 280 + distortion_limiting_factor: 220 + braidness: 20 + invert: false + + # 06 + - + width: 1024 + height: 1024 + max_hedgehogs: 64 + cell_size: 64 + distortion_limiting_factor: 150 + braidness: 3 + invert: true + + # 07 + - + width: 2048 + height: 1024 + max_hedgehogs: 64 + cell_size: 80 + distortion_limiting_factor: 150 + braidness: 5 + invert: true + + # 08 + - + width: 2048 + height: 1024 + max_hedgehogs: 64 + cell_size: 120 + distortion_limiting_factor: 150 + braidness: 5 + invert: true + + # 09 + - + width: 4096 + height: 2048 + max_hedgehogs: 64 + cell_size: 140 + distortion_limiting_factor: 200 + braidness: 10 + invert: true + + # 10 + - + width: 4096 + height: 2048 + max_hedgehogs: 64 + cell_size: 220 + distortion_limiting_factor: 250 + braidness: 10 + invert: true + + # 11 + - + width: 4096 + height: 2048 + max_hedgehogs: 64 + cell_size: 256 + distortion_limiting_factor: 280 + braidness: 10 + invert: true + +template_types: + small_tunnels: [0, 1] + medium_tunnels: [2, 3] + large_tunnels: [4, 5] + small_islands: [6, 7] + medium_islands: [8, 9] + large_islands: [10, 11] + diff -r 2b4f361e3891 -r 629d5123a979 share/hedgewars/Data/wfc_templates.yaml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/share/hedgewars/Data/wfc_templates.yaml Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,196 @@ +--- +# Templates for wavefront collapse map generator in hedgewars + +templates: + - &template_00 + width: 3960 + height: 1920 + can_invert: false + is_negative: false + put_girders: true + max_hedgehogs: 40 + wrap: true + edges: + bottom: + name: "ff" + symmetrical: true + tiles: &template_00_tiles + - name: "120_bar.png" + edges: + top: + name: "ff" + symmetrical: true + right: + name: "fe" + bottom: + name: "ee" + symmetrical: true + left: + name: "fe" + reversed: true + is_negative: true + can_mirror: false + can_flip: false + can_rotate90: true + can_rotate180: true + can_rotate270: true + - name: "120_corner.png" + edges: + top: + name: "fe" + right: + name: "ee" + symmetrical: true + bottom: + name: "ee" + symmetrical: true + left: + name: "fe" + reversed: true + is_negative: true + can_mirror: false + can_flip: false + can_rotate90: true + can_rotate180: true + can_rotate270: true + - name: "120_corner.png" + edges: + top: + name: "fe" + reversed: true + right: + name: "ff" + symmetrical: true + bottom: + name: "ff" + symmetrical: true + left: + name: "fe" + is_negative: false + can_mirror: false + can_flip: false + can_rotate90: true + can_rotate180: true + can_rotate270: true + - name: "120_filled.png" + edges: + top: + name: "ff" + symmetrical: true + right: + name: "ff" + symmetrical: true + bottom: + name: "ff" + symmetrical: true + left: + name: "ff" + symmetrical: true + is_negative: true + can_mirror: false + can_flip: false + can_rotate90: false + can_rotate180: false + can_rotate270: false + - name: "120_filled.png" + edges: + top: + name: "ee" + symmetrical: true + right: + name: "ee" + symmetrical: true + bottom: + name: "ee" + symmetrical: true + left: + name: "ee" + symmetrical: true + is_negative: false + can_mirror: false + can_flip: false + can_rotate90: false + can_rotate180: false + can_rotate270: false + - name: "120_two_corners.png" + edges: + top: + name: "fe" + right: + name: "fe" + reversed: true + bottom: + name: "fe" + left: + name: "fe" + reversed: true + is_negative: true + can_mirror: true + can_flip: false + can_rotate90: false + can_rotate180: false + can_rotate270: false + + - &template_01 + width: 3960 + height: 1920 + can_invert: false + is_negative: false + put_girders: true + max_hedgehogs: 40 + wrap: false + edges: &open_edges + top: + name: "ee" + symmetrical: true + right: + name: "ee" + symmetrical: true + bottom: + name: "ff" + symmetrical: true + left: + name: "ee" + symmetrical: true + tiles: *template_00_tiles + + - &template_02 + width: 1200 + height: 600 + can_invert: false + is_negative: false + put_girders: true + max_hedgehogs: 24 + wrap: false + edges: *open_edges + tiles: *template_00_tiles + + - &template_03 + width: 720 + height: 7920 + can_invert: false + is_negative: false + put_girders: true + max_hedgehogs: 64 + wrap: false + edges: *open_edges + tiles: *template_00_tiles + + + - &template_04 + width: 2200 + height: 960 + can_invert: false + is_negative: false + put_girders: true + max_hedgehogs: 24 + wrap: false + edges: *open_edges + tiles: *template_00_tiles + +template_types: + small: [2] + medium: [4] + large: [1] + cavern: [0] + wacky: [3] diff -r 2b4f361e3891 -r 629d5123a979 tools/corrosion/.github/FUNDING.yml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/corrosion/.github/FUNDING.yml Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,1 @@ +github: ["jschwe"] diff -r 2b4f361e3891 -r 629d5123a979 tools/corrosion/.github/ISSUE_TEMPLATE/bug_report.yml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/corrosion/.github/ISSUE_TEMPLATE/bug_report.yml Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,81 @@ +name: Bug Report +description: File a bug report +title: "[Bug]: " +labels: ["bug", "triage"] +assignees: + - jschwe +body: + - type: markdown + attributes: + value: | + Thanks for taking the time to fill out this bug report! + - type: textarea + attributes: + label: Current Behavior + description: A concise description of what you're experiencing. + validations: + required: false + - type: textarea + attributes: + label: Expected Behavior + description: A concise description of what you expected to happen. + validations: + required: false + - type: textarea + attributes: + label: Steps To Reproduce + description: Steps to reproduce the behavior. + placeholder: | + 1. In this environment... + 2. With this config... + 3. Run '...' + 4. See error... + validations: + required: false + - type: textarea + attributes: + label: Environment + description: | + examples: + - **OS**: Ubuntu 22.04 + - **CMake**: 3.22.0 + - **CMake Generator**: Ninja 1.11 + value: | + - OS: + - CMake: + - CMake Generator: + render: markdown + validations: + required: false + - type: textarea + attributes: + label: CMake configure log with Debug log-level + description: | + Output when configuring with `cmake -S -B --log-level=DEBUG `: +
CMake configure log +

+ + ``` + + ``` + +

+
+ validations: + required: false + - type: textarea + attributes: + label: CMake Build step log + description: | + Output when building with `cmake --build --verbose`: +
CMake build log +

+ + ``` + + ``` + +

+
+ validations: + required: false diff -r 2b4f361e3891 -r 629d5123a979 tools/corrosion/.github/scripts/determine_compiler.sh --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/corrosion/.github/scripts/determine_compiler.sh Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,59 @@ +#!/usr/bin/env bash + +compiler_kind="$1" +runner_os="$2" +target_abi="$3" +target_system_name="$4" +target_arch="$5" + +set -e + +if [[ -z "$GITHUB_OUTPUT" ]]; then + echo "Error: This script should only be run in github actions environment" + exit 1 +fi +if [[ -z "${runner_os}" || -z "${target_abi}" || -z "${target_arch}" ]]; then + echo "Error: Not all required parameters where set" + exit 1 +fi +if [[ -z "${compiler_kind}" || "${compiler_kind}" == "default" ]]; then + echo "compiler option was not set. Determining default compiler." + if [[ "${runner_os}" == "Windows" ]]; then + if [[ "${target_abi}" == "msvc" ]]; then + compiler_kind=msvc + elif [[ "${target_abi}" == "gnu" ]]; then + compiler_kind=gcc + else + echo "Unknown abi for Windows: ${target_abi}" + exit 1 + fi + elif [[ "${runner_os}" == "macOS" ]]; then + compiler_kind="clang" + elif [[ "${runner_os}" == "Linux" ]]; then + compiler_kind="gcc" + else + echo "Unknown Runner OS: ${runner_os}" + exit 1 + fi +fi +echo "Compiler Family: '${compiler_kind}'" + +if [[ "${compiler_kind}" == "clang" ]]; then + c_compiler="clang" + cxx_compiler="clang++" +elif [[ "${compiler_kind}" == "msvc" ]]; then + c_compiler="cl" + cxx_compiler="cl" +elif [[ "${compiler_kind}" == "gcc" ]]; then + if [[ -z "${target_system_name}" ]]; then + c_compiler="gcc" + cxx_compiler="g++" + else + c_compiler="${target_arch}-linux-gnu-gcc" + cxx_compiler="${target_arch}-linux-gnu-g++" + fi +fi +echo "Chose C compiler: '${c_compiler}'" +echo "Chose C++ compiler: '${cxx_compiler}'" +echo "c_compiler=-DCMAKE_C_COMPILER=${c_compiler}" >> $GITHUB_OUTPUT +echo "cxx_compiler=-DCMAKE_CXX_COMPILER=${cxx_compiler}" >> $GITHUB_OUTPUT diff -r 2b4f361e3891 -r 629d5123a979 tools/corrosion/.github/scripts/toolchains/aarch64-apple-darwin-clang.cmake --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/corrosion/.github/scripts/toolchains/aarch64-apple-darwin-clang.cmake Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,5 @@ +set(CMAKE_C_COMPILER "clang") +set(CMAKE_CXX_COMPILER "clang++") +set(CMAKE_C_COMPILER_TARGET "aarch64-apple-darwin") +set(CMAKE_CXX_COMPILER_TARGET "aarch64-apple-darwin") +set(CMAKE_OSX_ARCHITECTURES "arm64" CACHE STRING "") diff -r 2b4f361e3891 -r 629d5123a979 tools/corrosion/.github/scripts/toolchains/aarch64-unknown-linux-gnu-clang.cmake --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/corrosion/.github/scripts/toolchains/aarch64-unknown-linux-gnu-clang.cmake Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,4 @@ +set(CMAKE_C_COMPILER "clang") +set(CMAKE_CXX_COMPILER "clang++") +set(CMAKE_C_COMPILER_TARGET "aarch64-linux-gnu") +set(CMAKE_CXX_COMPILER_TARGET "aarch64-linux-gnu") diff -r 2b4f361e3891 -r 629d5123a979 tools/corrosion/.github/scripts/toolchains/aarch64-unknown-linux-gnu-gcc.cmake --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/corrosion/.github/scripts/toolchains/aarch64-unknown-linux-gnu-gcc.cmake Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,3 @@ +set(CMAKE_C_COMPILER "aarch64-linux-gnu-gcc") +set(CMAKE_CXX_COMPILER "aarch64-linux-gnu-g++") +set(CMAKE_SYSTEM_NAME "Linux") diff -r 2b4f361e3891 -r 629d5123a979 tools/corrosion/.github/scripts/toolchains/i686-unknown-linux-gnu-clang.cmake --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/corrosion/.github/scripts/toolchains/i686-unknown-linux-gnu-clang.cmake Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,4 @@ +set(CMAKE_C_COMPILER "clang") +set(CMAKE_CXX_COMPILER "clang++") +set(CMAKE_C_COMPILER_TARGET "i686-pc-linux-gnu") +set(CMAKE_CXX_COMPILER_TARGET "i686-pc-linux-gnu") diff -r 2b4f361e3891 -r 629d5123a979 tools/corrosion/.github/scripts/toolchains/i686-unknown-linux-gnu-gcc.cmake --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/corrosion/.github/scripts/toolchains/i686-unknown-linux-gnu-gcc.cmake Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,3 @@ +set(CMAKE_C_COMPILER "i686-linux-gnu-gcc") +set(CMAKE_CXX_COMPILER "i686-linux-gnu-g++") +set(CMAKE_SYSTEM_NAME "Linux") diff -r 2b4f361e3891 -r 629d5123a979 tools/corrosion/.github/scripts/toolchains/x86_64-apple-darwin-clang.cmake --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/corrosion/.github/scripts/toolchains/x86_64-apple-darwin-clang.cmake Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,7 @@ +set(CMAKE_C_COMPILER "clang") +set(CMAKE_CXX_COMPILER "clang++") +set(CMAKE_C_COMPILER_TARGET "x86_64-apple-darwin") +set(CMAKE_CXX_COMPILER_TARGET "x86_64-apple-darwin") +set(CMAKE_SYSTEM_NAME "Darwin") +set(CMAKE_SYSTEM_VERSION ${CMAKE_HOST_SYSTEM_VERSION}) +set(CMAKE_OSX_ARCHITECTURES "x86_64" CACHE STRING "") diff -r 2b4f361e3891 -r 629d5123a979 tools/corrosion/.github/scripts/toolchains/x86_64-pc-windows-gnullvm.cmake --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/corrosion/.github/scripts/toolchains/x86_64-pc-windows-gnullvm.cmake Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,4 @@ +set(CMAKE_C_COMPILER "clang") +set(CMAKE_CXX_COMPILER "clang++") +set(CMAKE_C_COMPILER_TARGET "x86_64-pc-windows-gnu") +set(CMAKE_CXX_COMPILER_TARGET "x86_64-pc-windows-gnu") diff -r 2b4f361e3891 -r 629d5123a979 tools/corrosion/.github/scripts/toolchains/x86_64-unknown-linux-gnu-clang.cmake --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/corrosion/.github/scripts/toolchains/x86_64-unknown-linux-gnu-clang.cmake Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,3 @@ +# Assumption: This is the native host target. +set(CMAKE_C_COMPILER "clang") +set(CMAKE_CXX_COMPILER "clang++") diff -r 2b4f361e3891 -r 629d5123a979 tools/corrosion/.github/scripts/toolchains/x86_64-unknown-linux-gnu-gcc.cmake --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/corrosion/.github/scripts/toolchains/x86_64-unknown-linux-gnu-gcc.cmake Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,3 @@ +# Assumption: This is the native host target. +set(CMAKE_C_COMPILER "gcc") +set(CMAKE_CXX_COMPILER "g++") diff -r 2b4f361e3891 -r 629d5123a979 tools/corrosion/.github/workflows/gh-pages.yaml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/corrosion/.github/workflows/gh-pages.yaml Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,78 @@ +name: Deploy GH pages +on: + push: + branches: + - master + # Allows you to run this workflow manually from the Actions tab + workflow_dispatch: + +# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages +permissions: + contents: read + pages: write + id-token: write + +# Allow one concurrent deployment +concurrency: + group: "pages" + cancel-in-progress: true + +jobs: + # Build and deploy the documentation of master and the stable/v0.5 branch + deploy: + runs-on: ubuntu-latest + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + steps: + - name: Install mdbook + env: + MDBOOK_VERSION: 'v0.4.27' + run: | + mkdir mdbook + curl -sSL https://github.com/rust-lang/mdBook/releases/download/${MDBOOK_VERSION}/mdbook-${MDBOOK_VERSION}-x86_64-unknown-linux-gnu.tar.gz | tar -xz --directory=./mdbook + echo `pwd`/mdbook >> $GITHUB_PATH + - name: Checkout master + uses: actions/checkout@v4 + with: + path: main + - name: Checkout stable/v0.5 + uses: actions/checkout@v4 + with: + path: stable-v0.5 + ref: 'stable/v0.5' + - name: Setup Pages + uses: actions/configure-pages@v3 + - name: Build mdbook for main branch + working-directory: 'main/doc' + run: mdbook build + - name: Build mdbook for stable/v0.5 branch + working-directory: 'stable-v0.5/doc' + run: mdbook build + # Override mdbooks default highlight.js with a custom version containing CMake support. + - uses: actions/checkout@v4 + with: + repository: 'highlightjs/highlight.js' + # mdbook currently (as of v0.4.27) does not support v11 yet. + ref: '10.7.3' + path: highlightjs + - name: Build custom highlight.js + run: | + npm install + node tools/build.js :common cmake yaml + working-directory: highlightjs + - name: Override highlightjs + run: | + cp highlightjs/build/highlight.min.js main/doc/book/highlight.js + cp highlightjs/build/highlight.min.js stable-v0.5/doc/book/highlight.js + - name: Copy stable doc into main + run: mkdir main/doc/book/v0.5 && cp -a stable-v0.5/doc/book/. main/doc/book/v0.5/ + - name: Debug print + run: ls -la main/doc/book/v0.5 + - name: Upload artifact + uses: actions/upload-pages-artifact@v2 + with: + path: 'main/doc/book' + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v2 diff -r 2b4f361e3891 -r 629d5123a979 tools/corrosion/.github/workflows/linux.yaml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/corrosion/.github/workflows/linux.yaml Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,58 @@ +# Workflow file for Linux hosts +name: Corrosion on Linux +on: + workflow_call: + inputs: + ubuntu_version: + required: false + type: string + default: "latest" + cmake: + required: false + type: string + default: "3.22.6" + generator: + required: true + type: string + c_compiler: + required: true + type: string + rust: + required: false + type: string + default: 1.46.0 + target_arch: + required: false + type: string + default: x86_64 + +jobs: + linux: + name: Test Linux + runs-on: ubuntu-${{ inputs.ubuntu_version }} + steps: + - uses: actions/checkout@v4 + - name: Install CMake + uses: lukka/get-cmake@519de0c7b4812477d74976b2523a9417f552d126 + with: + cmakeVersion: "${{ inputs.cmake }}" + ninjaVersion: "~1.10.0" + - name: Install Rust + id: install_rust + uses: dtolnay/rust-toolchain@master + with: + toolchain: ${{inputs.rust}} + targets: ${{inputs.target_arch}}-unknown-linux-gnu + - name: Install Cross Compiler + shell: bash + run: | + echo "::group::apt-install" + sudo apt-get update + sudo apt-get install -y "g++-${{inputs.target_arch}}-linux-gnu" + echo "::endgroup::" + if: ${{ 'Linux' == runner.os && inputs.target_arch != 'x86_64' }} + - name: Configure Corrosion + run: cmake -S. -Bbuild -G "${{ inputs.generator }}" "-DRust_TOOLCHAIN=${{steps.install_rust.outputs.name}}" --preset "${{ inputs.target_arch }}-unknown-linux-gnu-${{ inputs.c_compiler }}" + - name: Run Tests + working-directory: build + run: ctest --output-on-failure --build-config Debug -j 3 diff -r 2b4f361e3891 -r 629d5123a979 tools/corrosion/.github/workflows/test.yaml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/corrosion/.github/workflows/test.yaml Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,454 @@ +name: Tests +on: + push: + branches: + - master + pull_request: + branches: + - 'master' + - 'stable/**' +jobs: + + visual_studio_base: + name: Test Visual Studio (base) + uses: ./.github/workflows/visual_studio.yaml + with: + vs_version: "2022" + rust: 1.46.0 + + visual_studio_stage2: + name: Test Visual Studio + uses: ./.github/workflows/visual_studio.yaml + needs: + - visual_studio_base + strategy: + matrix: + vs_version: + - "2019" + - "2022" + arch: + - x86_64 + - i686 + - aarch64 + rust: + - "1.54.0" + include: + - arch: x86_64 + vs_version: 2022 + rust: stable + - arch: x86_64 + vs_version: 2022 + rust: nightly + with: + vs_version: "${{ matrix.vs_version}}" + rust: 1.54.0 + target_arch: "${{ matrix.arch}}" + + windows_ninja_cl: + name: Test Windows Ninja MSVC + runs-on: ${{ matrix.os }} + needs: + - visual_studio_base + strategy: + fail-fast: false + matrix: + os: + - windows-2022 + arch: + - x86_64 + - i686 + - aarch64 + compiler: + - cl + - clang-cl + - clang + include: + - os: windows-2022 + vs_version: vs-2022 + cmake: 3.22.6 + - rust: 1.54.0 + # Add variable mapping for ilammy/msvc-dev-cmd action + - arch: x86_64 + msvc_dev_arch: amd64 + - arch: i686 + msvc_dev_arch: amd64_x86 + - arch: aarch64 + msvc_dev_arch: amd64_arm64 + exclude: + # Not sure what parameters CMake needs when cross-compiling with clang-cl, so exclude for now + - compiler: clang-cl + arch: i686 + - compiler: clang-cl + arch: aarch64 + - compiler: clang + arch: i686 + - compiler: clang + arch: aarch64 + + steps: + - uses: actions/checkout@v4 + - name: Install CMake + uses: lukka/get-cmake@519de0c7b4812477d74976b2523a9417f552d126 + with: + cmakeVersion: "${{ matrix.cmake }}" + ninjaVersion: "~1.10.0" + - name: Install Rust + id: install_rust + uses: dtolnay/rust-toolchain@master + with: + toolchain: ${{matrix.rust}} + targets: ${{matrix.arch}}-pc-windows-msvc + - name: Setup MSVC Development Environment + uses: ilammy/msvc-dev-cmd@v1 + with: + arch: ${{ matrix.msvc_dev_arch }} + - name: Configure + run: cmake -S. -Bbuild "-DRust_TOOLCHAIN=${{steps.install_rust.outputs.name}}" --preset "ninja-${{ matrix.arch }}-pc-windows-msvc-${{ matrix.compiler }}" + - name: Run Tests + working-directory: build + run: ctest --output-on-failure --build-config Debug -j 3 + + windows_gnu: + name: Test Windows GNU + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: + - windows-2022 + arch: + - x86_64 + # - i686 + # - aarch64 + compiler: + - gcc # Clang only has experimental support for Cygwin / MinGW, so we don't test it + generator: + - ninja + - make + include: + - cmake: 3.22.6 + - rust: 1.54.0 + + steps: + - uses: actions/checkout@v4 + - name: Install CMake + uses: lukka/get-cmake@519de0c7b4812477d74976b2523a9417f552d126 + with: + cmakeVersion: "${{ matrix.cmake }}" + ninjaVersion: "~1.10.0" + - name: Install Rust + id: install_rust + uses: dtolnay/rust-toolchain@master + with: + toolchain: ${{matrix.rust}} + targets: ${{matrix.arch}}-pc-windows-gnu + - name: Configure + run: cmake -S. -Bbuild "-DRust_TOOLCHAIN=${{steps.install_rust.outputs.name}}" --preset "${{ matrix.generator }}-${{ matrix.arch }}-pc-windows-gnu-${{ matrix.compiler }}" + - name: Run Tests + working-directory: build + run: ctest --output-on-failure --build-config Debug -j 3 + + windows_gnullvm_msys2: + name: Test Windows gnullvm on msys2 + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: + - windows-2022 + arch: + - x86_64 + # - i686 + # - aarch64 + generator: + - Ninja + - MSYS Makefiles + include: + - arch: x86_64 + msystem: CLANG64 +# - arch: i686 +# msystem: CLANG32 +# - arch: aarch64 +# msystem: CLANGARM64 + defaults: + run: + shell: msys2 {0} + steps: + - uses: actions/checkout@v4 + - name: Install Rust + id: install_rust + uses: dtolnay/rust-toolchain@master + with: + toolchain: stable + targets: ${{matrix.arch}}-pc-windows-gnullvm + - uses: msys2/setup-msys2@v2 + with: + msystem: ${{matrix.msystem}} + path-type: inherit + install: >- + git + make + pacboy: >- + toolchain:p + cmake:p + ninja:p + - name: Configure + run: cmake -S. -Bbuild -G "${{matrix.generator}}" --toolchain=.github/scripts/toolchains/${{matrix.arch}}-pc-windows-gnullvm.cmake + - name: Run Tests + working-directory: build + run: ctest --output-on-failure --build-config Debug -j 3 + +# For now just test if hostbuild works when cross-compiling on windows. +# For testing everything we would also need to install a cross-compiler first. + windows_cross_hostbuild: + name: Test Windows Cross + runs-on: windows-2022 + steps: + - uses: actions/checkout@v4 + - name: Install CMake + uses: lukka/get-cmake@519de0c7b4812477d74976b2523a9417f552d126 + with: + cmakeVersion: "~3.22.0" + ninjaVersion: "~1.10.0" + - name: Install Rust + id: install_rust + uses: dtolnay/rust-toolchain@master + with: + toolchain: stable + targets: aarch64-unknown-linux-gnu + - name: Configure + run: cmake -S. -Bbuild "-DRust_TOOLCHAIN=${{steps.install_rust.outputs.name}}" -DRust_CARGO_TARGET=aarch64-unknown-linux-gnu + - name: Run Tests + working-directory: build + run: ctest --output-on-failure --build-config Debug -R hostbuild + + linux_base: + name: Test Linux (base) + uses: ./.github/workflows/linux.yaml + with: + c_compiler: "gcc" + generator: "Ninja" + + linux_stage2: + name: Test Linux + needs: + - linux_base + uses: ./.github/workflows/linux.yaml + with: + target_arch: "${{ matrix.arch }}" + c_compiler: "${{ matrix.compiler }}" + generator: "${{ matrix.generator }}" + strategy: + fail-fast: false + matrix: + arch: + - x86_64 + - i686 + - aarch64 + compiler: + - gcc + generator: + - "Ninja" + - "Unix Makefiles" + include: + # rustc doesn't support cross-compiling with clang out of the box, since + # clang requires a --target parameter. Corrosion currently can only pass + # this for the top-level crate, so linking of cdylibs that are built as + # dependencies of this crate will fail if they exist. + # Solutions would be to make cross-compiling with clang work out-of-the-box + # in rustc, or working around it in corrosion by adding a linker-wrapper. + # For this reason we only test clang with the host target for now. + - arch: x86_64 + compiler: clang + generator: "Ninja" + - arch: x86_64 + generator: "Ninja Multi-Config" + compiler: gcc + + darwin: + name: Test MacOS + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + arch: + - x86_64 + - aarch64 + compiler: + - clang + generator: + - "Ninja" + - "Xcode" + include: + - os: macos-latest + - cmake: 3.22.6 + - rust: 1.54.0 + + steps: + - uses: actions/checkout@v4 + - name: Install CMake + uses: lukka/get-cmake@519de0c7b4812477d74976b2523a9417f552d126 + with: + cmakeVersion: "${{ matrix.cmake }}" + ninjaVersion: "~1.10.0" + # Install cbindgen before Rust to use recent default Rust version. + - name: Install cbindgen + run: cargo install cbindgen + - name: Install Rust + id: install_rust + uses: dtolnay/rust-toolchain@master + with: + toolchain: ${{matrix.rust}} + targets: ${{matrix.arch}}-apple-darwin + - name: Configure + run: cmake -S. -Bbuild --log-level=DEBUG -G "${{ matrix.generator }}" "-DRust_TOOLCHAIN=${{steps.install_rust.outputs.name}}" --preset "${{ matrix.arch }}-apple-darwin-${{ matrix.compiler }}" + - name: Run Tests + working-directory: build + run: ctest --output-on-failure --build-config Debug -j 3 + + + test_cxxbridge: + name: Test cxxbridge integration + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: + - windows-2022 + - ubuntu-latest + - macos-13 + include: + # Should be in sync with the `cxx` version the Carg.lock of the cxxbridge tests, + # otherwise the caching will not work and the cmd will be built from source. + - cxxbridge_version: "1.0.86" + steps: + - uses: actions/checkout@v4 + - uses: actions/cache@v4 + id: cache_cxxbridge + with: + path: "~/.cargo/bin/cxxbridge*" + key: ${{ runner.os }}-cxxbridge_${{ matrix.cxxbridge_version }} + - name: Install cxxbridge + if: steps.cache_cxxbridge.outputs.cache-hit != 'true' + run: cargo install cxxbridge-cmd@${{ matrix.cxxbridge_version }} + - name: Install lld + run: sudo apt update && sudo apt install -y lld + if: ${{ 'Linux' == runner.os }} + - name: Setup MSVC Development Environment + uses: ilammy/msvc-dev-cmd@v1 + if: runner.os == 'Windows' + - name: Install CMake + uses: lukka/get-cmake@519de0c7b4812477d74976b2523a9417f552d126 + with: + cmakeVersion: "~3.22.0" + ninjaVersion: "~1.10.0" + - name: Install Rust + uses: dtolnay/rust-toolchain@master + with: + toolchain: stable minus 2 releases + - name: Configure + run: > + cmake + -S. + -Bbuild + -GNinja + -DCORROSION_VERBOSE_OUTPUT=ON + -DCORROSION_TESTS_CXXBRIDGE=ON + - name: Run Tests + working-directory: build + run: ctest --output-on-failure --build-config Debug -j 3 -R "^cxxbridge" + + autoinstall_cargo_target: + name: Test Auto-installing Cargo target via rustup + runs-on: ubuntu-22.04 + steps: + - uses: actions/checkout@v4 + - name: Install CMake + uses: lukka/get-cmake@519de0c7b4812477d74976b2523a9417f552d126 + - name: Install Rust + id: install_rust + uses: dtolnay/rust-toolchain@stable + - name: Install Cross Compiler + shell: bash + run: | + echo "::group::apt-install" + sudo apt-get update + sudo apt-get install -y gcc-aarch64-linux-gnu + echo "::endgroup::" + - name: Assert rustup target is not installed + run: rustup show | ( ! grep aarch64) + - name: Configure Corrosion + run: cmake -S. -Bbuild -GNinja -DRust_RUSTUP_INSTALL_MISSING_TARGET=ON --preset "aarch64-unknown-linux-gnu-gcc" + - name: Check rustup target is installed after configuring + run: rustup show | grep aarch64 + + install: + name: Test Corrosion as a Library + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: + - windows-2022 + - ubuntu-latest + - macos-13 + include: + - rust: 1.54.0 + + steps: + - uses: actions/checkout@v4 + - name: Setup MSVC Development Environment + uses: ilammy/msvc-dev-cmd@v1 + if: runner.os == 'Windows' + - name: Install CMake + uses: lukka/get-cmake@519de0c7b4812477d74976b2523a9417f552d126 + with: + cmakeVersion: "~3.22.0" + ninjaVersion: "~1.10.0" + # Install cbindgen before Rust to use recent default Rust version. + - name: Install cbindgen + run: cargo install cbindgen + - name: Install Rust + uses: dtolnay/rust-toolchain@master + with: + toolchain: ${{matrix.rust}} + - name: Test Corrosion as installed module + run: > + cmake + -S. + -Bbuild + -GNinja + -DCORROSION_VERBOSE_OUTPUT=ON + -DCMAKE_BUILD_TYPE=Release + -DCORROSION_TESTS_INSTALL_CORROSION=ON + && + cd build + && + ctest --output-on-failure -C Release -j 3 + + # We want an "accumulation" job here because it is easier to specify required + # jobs here via needs, then in the github UI, since we use matrix jobs. + ci-success: + name: bors-ci-status + if: ${{ always() }} + needs: + - visual_studio_stage2 + - windows_ninja_cl + - windows_gnu + - windows_gnullvm_msys2 + - linux_stage2 + - darwin + - test_cxxbridge + - autoinstall_cargo_target + - install + runs-on: ubuntu-latest + # Step copied from: https://github.com/cross-rs/cross/blob/80c9f9109a719ffb0f694060ddc6e371d5b3a540/.github/workflows/ci.yml#L361 + steps: + - name: Result + run: | + jq -C <<< "${needs}" + # Check if all needs were successful or skipped. + "$(jq -r 'all(.result as $result | (["success", "skipped"] | contains([$result])))' <<< "${needs}")" + env: + needs: ${{ toJson(needs) }} + diff -r 2b4f361e3891 -r 629d5123a979 tools/corrosion/.github/workflows/visual_studio.yaml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/corrosion/.github/workflows/visual_studio.yaml Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,53 @@ +name: Corrosion with Visual Studio + +on: + workflow_call: + inputs: + vs_version: + required: true + type: string + default: 2022 + cmake: + required: false + type: string + default: "3.22.6" + rust: + required: false + type: string + default: 1.46.0 + target_arch: + required: false + type: string + default: x86_64 + +jobs: + visual_studio: + name: Test Visual Studio ${{ inputs.vs_version }} + runs-on: "windows-${{ inputs.vs_version }}" + steps: + - uses: actions/checkout@v4 + - name: Install CMake + uses: lukka/get-cmake@519de0c7b4812477d74976b2523a9417f552d126 + with: + cmakeVersion: "${{ inputs.cmake }}" + ninjaVersion: "~1.10.0" + - name: Install Rust + id: install_rust + uses: dtolnay/rust-toolchain@master + with: + toolchain: ${{inputs.rust}} + targets: ${{inputs.target_arch}}-pc-windows-msvc + # The initial configure for MSVC is quite slow, so we cache the build directory + # (including the build directories of the tests) since reconfiguring is + # significantly faster. + - name: Cache MSVC build directory + id: cache-msvc-builddir + uses: actions/cache@v4 + with: + path: build + key: ${{ inputs.os }}-${{ inputs.target_arch }}-${{ inputs.rust }}-msvc-${{ inputs.vs_version}}-build + - name: Configure + run: cmake -S. -Bbuild -DCORROSION_TESTS_KEEP_BUILDDIRS=ON "-DRust_TOOLCHAIN=${{steps.install_rust.outputs.name}}" --preset "vs-${{ inputs.vs_version }}-${{ inputs.target_arch }}" + - name: Run Tests + working-directory: build + run: ctest --output-on-failure --build-config Debug -j 3 diff -r 2b4f361e3891 -r 629d5123a979 tools/corrosion/.gitignore --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/corrosion/.gitignore Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,8 @@ + +**/target/ +**/*.rs.bk +build*/ +.vscode +.idea +cmake-build-* +test/test_header.cmake diff -r 2b4f361e3891 -r 629d5123a979 tools/corrosion/CMakeLists.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/corrosion/CMakeLists.txt Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,86 @@ +cmake_minimum_required(VERSION 3.22) +project(Corrosion + # Official releases will be major.minor.patch. When the `tweak` field is + # set it indicates that we are on a commit, that is not a officially + # tagged release. Users don't need to care about this, it is mainly to + # clearly see in configure logs which version was used, without needing to + # rely on `git`, since Corrosion may be installed or otherwise packaged. + VERSION 0.99.99 # 1.0-pre-release + LANGUAGES NONE + HOMEPAGE_URL "https://corrosion-rs.github.io/corrosion/" +) + +# ==== Corrosion Configuration ==== + +option( + CORROSION_BUILD_TESTS + "Build Corrosion test project" + ${PROJECT_IS_TOP_LEVEL} +) + +if (PROJECT_IS_TOP_LEVEL) + # We need to enable a language for corrosions test to work. + # For projects using corrosion this is not needed + enable_language(C) +endif() + +# This little bit self-hosts the Corrosion toolchain to build the generator +# tool. +# +# It is strongly encouraged to install Corrosion separately and use +# `find_package(Corrosion REQUIRED)` instead if that works with your workflow. +option(CORROSION_INSTALL_ONLY "Only add rules for installing Corrosion itself." OFF) +if (NOT CORROSION_INSTALL_ONLY) + list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake) + include(Corrosion) +endif() + +# Testing +if (CORROSION_BUILD_TESTS) + include(CTest) + add_subdirectory(test) +endif() + +# If Corrosion is a subdirectory, do not enable its install code +if (NOT PROJECT_IS_TOP_LEVEL) + return() +endif() + +# Installation + +include(GNUInstallDirs) + +# Generate the Config file +include(CMakePackageConfigHelpers) + +configure_package_config_file( + cmake/CorrosionConfig.cmake.in CorrosionConfig.cmake + INSTALL_DESTINATION + "${CMAKE_INSTALL_FULL_LIBDIR}/cmake/Corrosion" +) + +write_basic_package_version_file( + "${CMAKE_CURRENT_BINARY_DIR}/CorrosionConfigVersion.cmake" + VERSION ${PROJECT_VERSION} + COMPATIBILITY + SameMajorVersion + ARCH_INDEPENDENT +) + +install( + FILES + "${CMAKE_CURRENT_BINARY_DIR}/CorrosionConfig.cmake" + "${CMAKE_CURRENT_BINARY_DIR}/CorrosionConfigVersion.cmake" + DESTINATION + "${CMAKE_INSTALL_FULL_LIBDIR}/cmake/Corrosion" +) + +# These CMake scripts are needed both for the install and as a subdirectory +install( + FILES + cmake/Corrosion.cmake + cmake/CorrosionGenerator.cmake + cmake/FindRust.cmake + DESTINATION + "${CMAKE_INSTALL_FULL_DATADIR}/cmake" +) diff -r 2b4f361e3891 -r 629d5123a979 tools/corrosion/CMakePresets.json --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/corrosion/CMakePresets.json Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,290 @@ +{ + "version": 3, + "cmakeMinimumRequired": { + "major": 3, + "minor": 22, + "patch": 0 + }, + "configurePresets": [ + { + "name": "ninja", + "hidden": true, + "generator": "Ninja" + }, + { + "name": "ninja-mc", + "hidden": true, + "generator": "Ninja Multi-Config" + }, + { + "name": "make", + "hidden": true, + "generator": "Unix Makefiles" + }, + { + "name": "vs-2019", + "hidden": true, + "generator": "Visual Studio 16 2019" + }, + { + "name": "vs-2022", + "hidden": true, + "generator": "Visual Studio 17 2022" + }, + { + "name": "windows-only", + "hidden": true, + "condition": { + "type": "equals", + "lhs": "${hostSystemName}", + "rhs": "Windows" + } + }, + { + "name": "windows-10-cross", + "hidden": true, + "cacheVariables": { + "CMAKE_SYSTEM_NAME": "Windows", + "CMAKE_SYSTEM_VERSION": "10.0" + }, + "condition": { + "type": "equals", + "lhs": "${hostSystemName}", + "rhs": "Windows" + } + }, + { + "name": "x86_64-pc-windows-msvc", + "hidden": true, + "inherits": ["windows-only"], + "cacheVariables": { + "Rust_CARGO_TARGET": "x86_64-pc-windows-msvc" + } + }, + { + "name": "i686-pc-windows-msvc", + "hidden": true, + "cacheVariables": { + "Rust_CARGO_TARGET": "i686-pc-windows-msvc" + } + }, + { + "name": "aarch64-pc-windows-msvc", + "hidden": true, + "cacheVariables": { + "Rust_CARGO_TARGET": "aarch64-pc-windows-msvc" + } + }, + { + "name": "x86_64-unknown-linux-gnu", + "hidden": true, + "cacheVariables": { + "Rust_CARGO_TARGET": "x86_64-unknown-linux-gnu" + } + }, + { + "name": "i686-unknown-linux-gnu", + "hidden": true, + "cacheVariables": { + "Rust_CARGO_TARGET": "i686-unknown-linux-gnu" + } + }, + { + "name": "aarch64-unknown-linux-gnu", + "hidden": true, + "cacheVariables": { + "Rust_CARGO_TARGET": "aarch64-unknown-linux-gnu" + } + }, + { + "name": "x86_64-apple-darwin", + "hidden": true, + "cacheVariables": { + "Rust_CARGO_TARGET": "x86_64-apple-darwin" + } + }, + { + "name": "aarch64-apple-darwin", + "hidden": true, + "cacheVariables": { + "Rust_CARGO_TARGET": "aarch64-apple-darwin" + } + }, + { + "name": "vs-platform-arm64", + "hidden": true, + "inherits": ["aarch64-pc-windows-msvc","windows-10-cross"], + "architecture": { + "value": "ARM64" + } + }, + { + "name": "vs-platform-x64", + "hidden": true, + "inherits": ["x86_64-pc-windows-msvc"], + "architecture": { + "value": "x64" + } + }, + { + "name": "vs-platform-i686", + "hidden": true, + "inherits": ["i686-pc-windows-msvc", "windows-10-cross"], + "architecture": { + "value": "Win32" + } + }, + { + "name": "vs-2019-x86_64", + "inherits": ["vs-platform-x64", "vs-2019"] + }, + { + "name": "vs-2022-x86_64", + "inherits": ["vs-platform-x64", "vs-2022"] + }, + { + "name": "vs-2019-i686", + "inherits": ["vs-platform-i686", "vs-2019"] + }, + { + "name": "vs-2022-i686", + "inherits": ["vs-platform-i686", "vs-2022"] + }, + { + "name": "vs-2019-aarch64", + "inherits": ["vs-platform-arm64", "vs-2019"] + }, + { + "name": "vs-2022-aarch64", + "inherits": ["vs-platform-arm64", "vs-2022"] + }, + { + "name": "clang", + "hidden": true, + "cacheVariables": { + "CMAKE_C_COMPILER": "clang", + "CMAKE_CXX_COMPILER": "clang++" + } + }, + { + "name": "host-gcc", + "hidden": true, + "cacheVariables": { + "CMAKE_C_COMPILER": "gcc", + "CMAKE_CXX_COMPILER": "g++" + } + }, + { + "name": "clang-cl", + "hidden": true, + "inherits": ["windows-only"], + "cacheVariables": { + "CMAKE_C_COMPILER": "clang-cl", + "CMAKE_CXX_COMPILER": "clang-cl" + } + }, + { + "name": "cl", + "hidden": true, + "inherits": ["windows-only"], + "cacheVariables": { + "CMAKE_C_COMPILER": "cl", + "CMAKE_CXX_COMPILER": "cl" + } + }, + { + "name": "ninja-x86_64-pc-windows-msvc-cl", + "inherits": ["ninja", "x86_64-pc-windows-msvc", "cl"] + }, + { + "name": "ninja-x86_64-pc-windows-msvc-clang-cl", + "inherits": ["ninja", "x86_64-pc-windows-msvc", "clang-cl"] + }, + { + "name": "ninja-x86_64-pc-windows-msvc-clang", + "inherits": ["ninja", "x86_64-pc-windows-msvc", "clang"] + }, + { + "name": "ninja-i686-pc-windows-msvc-cl", + "inherits": ["ninja", "i686-pc-windows-msvc", "cl", "windows-10-cross"] + }, + { + "name": "ninja-i686-pc-windows-msvc-clang-cl", + "inherits": ["ninja", "i686-pc-windows-msvc", "clang-cl", "windows-10-cross"] + }, + { + "name": "ninja-i686-pc-windows-msvc-clang", + "inherits": ["ninja", "i686-pc-windows-msvc", "clang", "windows-10-cross"] + }, + { + "name": "ninja-aarch64-pc-windows-msvc-cl", + "inherits": ["ninja", "aarch64-pc-windows-msvc", "cl", "windows-10-cross"] + }, + { + "name": "ninja-aarch64-pc-windows-msvc-clang-cl", + "inherits": ["ninja", "aarch64-pc-windows-msvc", "clang-cl", "windows-10-cross"] + }, + { + "name": "ninja-aarch64-pc-windows-msvc-clang", + "inherits": ["ninja", "aarch64-pc-windows-msvc", "clang", "windows-10-cross"] + }, + { + "name": "ninja-x86_64-pc-windows-gnullvm", + "inherits": ["ninja", "windows-only", "clang"], + "toolchainFile": "${sourceDir}/.github/scripts/toolchains/x86_64-pc-windows-gnullvm.cmake" + }, + { + "name": "make-x86_64-pc-windows-gnullvm", + "inherits": ["make", "windows-only", "clang"], + "toolchainFile": "${sourceDir}/.github/scripts/toolchains/x86_64-pc-windows-gnullvm.cmake" + }, + { + "name": "ninja-x86_64-pc-windows-gnu-gcc", + "inherits": ["ninja", "host-gcc", "windows-only"] + }, + { + "name": "make-x86_64-pc-windows-gnu-gcc", + "inherits": ["make", "host-gcc", "windows-only"] + }, + { + "name": "x86_64-unknown-linux-gnu-clang", + "inherits": ["x86_64-unknown-linux-gnu"], + "toolchainFile": "${sourceDir}/.github/scripts/toolchains/${presetName}.cmake" + }, + { + "name": "x86_64-unknown-linux-gnu-gcc", + "inherits": ["x86_64-unknown-linux-gnu"], + "toolchainFile": "${sourceDir}/.github/scripts/toolchains/${presetName}.cmake" + }, + { + "name": "i686-unknown-linux-gnu-clang", + "inherits": ["i686-unknown-linux-gnu"], + "toolchainFile": "${sourceDir}/.github/scripts/toolchains/${presetName}.cmake" + }, + { + "name": "i686-unknown-linux-gnu-gcc", + "inherits": ["i686-unknown-linux-gnu"], + "toolchainFile": "${sourceDir}/.github/scripts/toolchains/${presetName}.cmake" + }, + { + "name": "aarch64-unknown-linux-gnu-clang", + "inherits": ["aarch64-unknown-linux-gnu"], + "toolchainFile": "${sourceDir}/.github/scripts/toolchains/${presetName}.cmake" + }, + { + "name": "aarch64-unknown-linux-gnu-gcc", + "inherits": ["aarch64-unknown-linux-gnu"], + "toolchainFile": "${sourceDir}/.github/scripts/toolchains/${presetName}.cmake" + }, + { + "name": "x86_64-apple-darwin-clang", + "inherits": ["x86_64-apple-darwin", "clang"], + "toolchainFile": "${sourceDir}/.github/scripts/toolchains/${presetName}.cmake" + }, + { + "name": "aarch64-apple-darwin-clang", + "inherits": ["aarch64-apple-darwin"], + "toolchainFile": "${sourceDir}/.github/scripts/toolchains/${presetName}.cmake" + } + ] +} diff -r 2b4f361e3891 -r 629d5123a979 tools/corrosion/LICENSE --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/corrosion/LICENSE Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2018 Andrew Gaspar + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff -r 2b4f361e3891 -r 629d5123a979 tools/corrosion/README.md --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/corrosion/README.md Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,50 @@ +# Corrosion +[![Build Status](https://github.com/corrosion-rs/corrosion/actions/workflows/test.yaml/badge.svg)](https://github.com/corrosion-rs/corrosion/actions?query=branch%3Amaster) +[![Documentation](https://img.shields.io/badge/docs-latest-blue.svg)](https://corrosion-rs.github.io/corrosion/) +![License](https://img.shields.io/badge/license-MIT-blue) + +Corrosion, formerly known as cmake-cargo, is a tool for integrating Rust into an existing CMake +project. Corrosion can automatically import executables, static libraries, and dynamic libraries +from a workspace or package manifest (`Cargo.toml` file). + +## Features +- Automatic Import of Executable, Static, and Shared Libraries from Rust Crate +- Easy Installation of Rust Executables +- Trivially Link Rust Executables to C/C++ Libraries in Tree +- Multi-Config Generator Support +- Simple Cross-Compilation + +## Sample Usage with FetchContent + +Using the CMake `FetchContent` module allows you to easily integrate corrosion into your build. +Other methods including installing corrosion or adding it as a subdirectory are covered in the +[setup chapter](https://corrosion-rs.github.io/corrosion/setup_corrosion.html) of the +corrosion [documentation](https://corrosion-rs.github.io/corrosion/). + +```cmake +include(FetchContent) + +FetchContent_Declare( + Corrosion + GIT_REPOSITORY https://github.com/corrosion-rs/corrosion.git + GIT_TAG v0.5 # Optionally specify a commit hash, version tag or branch here +) +FetchContent_MakeAvailable(Corrosion) + +# Import targets defined in a package or workspace manifest `Cargo.toml` file +corrosion_import_crate(MANIFEST_PATH rust-lib/Cargo.toml) + +add_executable(your_cpp_bin main.cpp) +target_link_libraries(your_cpp_bin PUBLIC rust-lib) +``` + +## Requirements + +### Stable v0.5 Release + +- CMake 3.15 or newer. Some features may only be available on more recent CMake versions +- Rust 1.46 or newer. Some platforms / features may require more recent Rust versions + +### Development (master branch) + +- CMake 3.22 or newer diff -r 2b4f361e3891 -r 629d5123a979 tools/corrosion/RELEASES.md --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/corrosion/RELEASES.md Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,477 @@ +# Unreleased + +### Breaking Changes + +- The master branch of corrosion now requires CMake 3.22. See also the + [v0.4.0 Release notes](#040-lts-2023-06-01) for more details. +- Removed native tooling and the corresponding option `CORROSION_NATIVE_TOOLING`. + Corrosion now always uses pure CMake. + +### New features + +- Support using the `$` generator expression in `OUTPUT_DIRECTORY`. [#459] +- Add `OVERRIDE_CRATE_TYPE` option to corrosion_import_crate, allowing users to override + the crate-types of Rust libraries (e.g. force building as a staticlib instead of an rlib). +- Support *-windows-gnullvm targets. +- experimental support in corrosion_install for installing libraries and header files + +[#459]: https://github.com/corrosion-rs/corrosion/pull/459 + +# v0.5.0 (2024-05-11) + +### Breaking Changes + +- Dashes (`-`) in names of imported CMake **library** targets are now replaced with underscores (`_`). + See [issue #501] for details. Users on older Corrosion versions will experience the same + change when using Rust 1.79 or newer. `bin` targets are not affected by this change. + +[issue #501]: https://github.com/corrosion-rs/corrosion/issues/501 + +# v0.4.10 (2024-05-11) + +### New features + +- `corrosion_experimental_cbindgen()` can now be called multiple times on the same Rust target, + as long as the output header name differs. This may be useful to generate separate C and C++ + bindings. [#507] +- If `corrosion_link_libraries()` is called on a Rust static library target, then + `target_link_libraries()` is called to propagate the dependencies to C/C++ consumers. + Previously a warning was emitted in this case and the arguments ignored. [#506] + +### Fixes + +- Combine `-framework` flags on macos to avoid linker deduplication errors [#455] +- `corrosion_experimental_cbindgen()` will now correctly use the package name, instead of assuming that + the package and crate name are identical. ([11e27c]) +- Set the `AR_` variable for `cc-rs` (except for msvc targets) [#456] +- Fix hostbuild when cross-compiling to windows [#477] +- Consider vworks executable suffix [#504] +- `corrosion_experimental_cbindgen()` now forwards the Rust target-triple (e.g. `aarch64-unknown-linux-gnu`) + to cbindgen via the `TARGET` environment variable. The `hostbuild` property is considered. [#507] +- Fix linking errors with Rust >= 1.79 and `-msvc` targets.` [#511] + + +[#455]: https://github.com/corrosion-rs/corrosion/pull/455 +[#456]: https://github.com/corrosion-rs/corrosion/pull/456 +[#477]: https://github.com/corrosion-rs/corrosion/pull/477 +[#504]: https://github.com/corrosion-rs/corrosion/pull/504 +[#506]: https://github.com/corrosion-rs/corrosion/pull/506 +[#507]: https://github.com/corrosion-rs/corrosion/pull/507 +[#511]: https://github.com/corrosion-rs/corrosion/pull/511 +[11e27c]: https://github.com/corrosion-rs/corrosion/pull/514/commits/11e27cde2cf32c7ed539c96eb03c2f10035de538 + +# v0.4.9 (2024-05-01) + +### New Features + +- Automatically detect Rust target for OpenHarmony ([#510]). + +### Fixes + +- Make find_package portable ([#509]). + +[#510]: https://github.com/corrosion-rs/corrosion/pull/510 +[#509]: https://github.com/corrosion-rs/corrosion/pull/509 + +# v0.4.8 (2024-04-03) + +### Fixes + +- Fix an internal error when passing both the `PROFILE` and `CRATES` option to + `corrosion_import_crate()` ([#496]). + +[#496]: https://github.com/corrosion-rs/corrosion/pull/496 + +# v0.4.7 (2024-01-19) + +### Fixes + +- The C/C++ compiler passed from corrosion to `cc-rs` can now be overridden by users setting + `CC_` (e.g. `CC_x86_64-unknown-linux-gnu=/path/to/my-compiler`) environment variables ([#475]). + +[#475]: https://github.com/corrosion-rs/corrosion/pull/475 + +# v0.4.6 (2024-01-17) + +### Fixes + +- Fix hostbuild executables when cross-compiling from non-windows to windows targets. + (Only with CMake >= 3.19). + +# v0.4.5 (2023-11-30) + +### Fixes + +- Fix hostbuild executables when cross-compiling on windows to non-windows targets + (Only with CMake >= 3.19). + +# v0.4.4 (2023-10-06) + +### Fixes + +- Add `chimera` ([#445]) and `unikraft` ([#446]) to the list of known vendors + +[#445]: https://github.com/corrosion-rs/corrosion/pull/445 +[#446]: https://github.com/corrosion-rs/corrosion/pull/446 + +# v0.4.3 (2023-09-09) + +### Fixes + +- Fix the PROFILE option with CMake < 3.19 [#427] +- Relax vendor parsing for espressif targets (removes warnings) +- Fix an issue detecting required link libraries with Rust >= 1.71 + when the cmake build directory is located in a Cargo workspace. + +# 0.4.2 (2023-07-16) + +### Fixes + +- Fix an issue when cross-compiling with clang +- Fix detecting required libraries with cargo 1.71 + +### New features + +- Users can now set `Rust_RESOLVE_RUSTUP_TOOLCHAINS` to `OFF`, which will result in Corrosion + not attempting to resolve rustc/cargo. + +# 0.4.1 (2023-06-03) + +This is a bugfix release. + +### Fixes + +- Fixes a regression on multi-config Generators + +# 0.4.0 LTS (2023-06-01) + +No changes compared to v0.4.0-beta2. + +## Announcements + +The `v0.4.x` LTS series will be the last release to support older CMake and Rust versions. +If necessary, fixes will be backported to the v0.4 branch. New features will not be +actively backported after the next major release, but community contributions are possible. +The `v0.4.x` series is currently planned to be maintained until the end of 2024. + +The following major release will increase the minimum required CMake version to 3.22. The +minimum supported Rust version will also be increased to make use of newly added flags, but +the exact version is not fixed yet. + + +## Changes compared to v0.3.5: + +### Breaking Changes + +- The Visual Studio Generators now require at least CMake 3.20. + This was previously announced in the 0.3.0 release notes and is the same + requirement as for the other Multi-Config Generators. +- The previously deprecated function `corrosion_set_linker_language()` + will now raise an error when called and may be removed without further + notice in future stable releases. Use `corrosion_set_linker()` instead. +- Improved the FindRust target triple detection, which may cause different behavior in some cases. + The detection does not require an enabled language anymore and will always fall back + to the default host target triple. A warning is issued if target triple detection failed. + +### Potentially Breaking Changes + +- Corrosion now sets the `IMPORTED_NO_SONAME` property for shared rust libraries, since by + default they won't have an `soname` field. + If you add a rustflag like `-Clink-arg=-Wl,-soname,libmycrate.so` in your project, + you should set this property to false on the shared rust library. +- Corrosion now uses a mechanism to determine which native libraries need to be linked with + Rust `staticlib` targets into C/C++ targets. The previous mechanism contained a hardcoded list. + The new mechanism asks `rustc` which libraries are needed at minimum for a given + target triple (with `std` support). This should not be a breaking change, but if you + do encounter a new linking issue when upgrading with `staticlib` targets, please open an + issue. + +### New features + +- `corrosion_import_crate()` has two new options `LOCKED` and `FROZEN` which pass the + `--locked` and `--frozen` flags to all invocations of cargo. +- `FindRust` now provides cache variables containing information on the default host + target triple: + - `Rust_CARGO_HOST_ARCH` + - `Rust_CARGO_HOST_VENDOR` + - `Rust_CARGO_HOST_OS` + - `Rust_CARGO_HOST_ENV` + +### Other changes + +- When installing Corrosion with CMake >= 3.19, the legacy Generator tool is + no longer built and installed by default. +- Corrosion now issues a warning when setting the linker or setting linker + options for a Rust static library. +- Corrosion no longer enables the `C` language when CMake is in crosscompiling mode and + no languages where previously enabled. This is not considered a breaking change. +- `corrosion_import_crate()` now warns about unexpected arguments. + +### Fixes + +- Fix building when the `dev` profile is explicitly set by the user. + +## Experimental features (may be changed or removed without a major version bump) + +- Experimental cxxbridge and cbindgen integration. +- Add a helper function to parse the package version from a Cargo.toml file +- Expose rustup toolchains discovered by `FindRust` in the following cache variables + which contain a list. + - `Rust_RUSTUP_TOOLCHAINS`: List of toolchains names + - `Rust_RUSTUP_TOOLCHAINS_VERSION`: List of `rustc` version of the toolchains + - `Rust_RUSTUP_TOOLCHAINS_RUSTC_PATH`: List of the path to `rustc` + - `Rust_RUSTUP_TOOLCHAINS_CARGO_PATH`: List of the path to `cargo`. Entries may be `NOTFOUND` if cargo + is not available for that toolchain. +- Add target properties `INTERFACE_CORROSION_RUSTC` and `INTERFACE_CORROSION_CARGO`, which may + be set to paths to `rustc` and `cargo` respectively to override the toolchain for a specific + target. + +# 0.3.5 (2023-03-19) + +- Fix building the Legacy Generator on Rust toolchains < 1.56 ([#365]) + +[#365]: https://github.com/corrosion-rs/corrosion/pull/365 + +# 0.3.4 (2023-03-02) + +## Fixes + +- Fix hostbuild (when CMake/Cargo is configured for cross-compiling) if clang is used ([#338]). + +## Other + +- Pass `--no-deps` to cargo metadata ([#334]). +- Bump the legacy generator dependencies + +[#334]: https://github.com/corrosion-rs/corrosion/pull/334 +[#338]: https://github.com/corrosion-rs/corrosion/pull/338 + + +# 0.3.3 (2023-02-17) + +## New features (Only available on CMake >= 3.19) + +- Add new `IMPORTED_CRATES` flag to `corrosion_import_crate()` to retrieve the list of imported crates in the current + scope ([#312](https://github.com/corrosion-rs/corrosion/pull/312)). + +## Fixes + +- Fix imported location target property when the rust target name contains dashes + and a custom OUTPUT_DIRECTORY was specified by the user ([#322](https://github.com/corrosion-rs/corrosion/pull/322)). +- Fix building for custom rust target-triples ([#316](https://github.com/corrosion-rs/corrosion/pull/316)) + +# 0.3.2 (2023-01-11) + +## New features (Only available on CMake >= 3.19) + +- Add new `CRATE_TYPES` flag to `corrosion_import_crate()` to restrict which + crate types should be imported ([#269](https://github.com/corrosion-rs/corrosion/pull/269)). +- Add `NO_LINKER_OVERRIDE` flag to let Rust choose the default linker for the target + instead of what Corrosion thinks is the appropriate linker driver ([#272](https://github.com/corrosion-rs/corrosion/pull/272)). + +## Fixes + +- Fix clean target when cross-compiling ([#291](https://github.com/corrosion-rs/corrosion/pull/291)). +- Don't set the linker for Rust static libraries ([#275](https://github.com/corrosion-rs/corrosion/pull/275)). +- Minor fixes in FindRust [#297](https://github.com/corrosion-rs/corrosion/pull/297): + - fix a logic error in the version detection + - fix a logic error in `QUIET` mode when rustup is not found. + +# 0.3.1 (2022-12-13) + +### Fixes + +- Fix a regression in detecting the MSVC abi ([#256]) +- Fix an issue on macOS 13 which affected rust crates compiling C++ code in build scripts ([#254]). +- Fix corrosion not respecting `CMAKE__OUTPUT_DIRECTORY` values ([#268]). +- Don't override rusts linker choice for the msvc abi (previously this was only skipped for msvc generators) ([#271]) + +[#254]: https://github.com/corrosion-rs/corrosion/pull/254 +[#256]: https://github.com/corrosion-rs/corrosion/pull/256 +[#268]: https://github.com/corrosion-rs/corrosion/pull/268 +[#271]: https://github.com/corrosion-rs/corrosion/pull/271 + +# 0.3.0 (2022-10-31) + +## Breaking + +- The minimum supported rust version (MSRV) was increased to 1.46, due to a cargo issue that recently + surfaced on CI when using crates.io. On MacOS 12 and Windows-2022 at least Rust 1.54 is required. +- MacOS 10 and 11 are no longer officially supported and untested in CI. +- The minimum required CMake version is now 3.15. +- Adding a `PRE_BUILD` custom command on a `cargo-build_` CMake target will no + longer work as expected. To support executing user defined commands before cargo build is + invoked users should use the newly added targets `cargo-prebuild` (before all cargo build invocations) + or `cargo-prebuild_` as a dependency target. + Example: `add_dependencies(cargo-prebuild code_generator_target)` + +### Breaking: Removed previously deprecated functionality +- Removed `add_crate()` function. Use `corrosio_import_crate()` instead. +- Removed `cargo_link_libraries()` function. Use `corrosion_link_libraries()` instead. +- Removed experimental CMake option `CORROSION_EXPERIMENTAL_PARSER`. + The corresponding stable option is `CORROSION_NATIVE_TOOLING` albeit with inverted semantics. +- Previously Corrosion would set the `HOST_CC` and `HOST_CXX` environment variables when invoking + cargo build, if the environment variables `CC` and `CXX` outside of CMake where set. + However this did not work as expected in all cases and sometimes the `HOST_CC` variable would be set + to a cross-compiler for unknown reasons. For this reason `HOST_CC` and `HOST_CXX` are not set by + corrosion anymore, but users can still set them manually if required via `corrosion_set_env_vars()`. +- The `CARGO_RUST_FLAGS` family of cache variables were removed. Corrosion does not internally use them + anymore. + +## Potentially breaking + +- The working directory when invoking `cargo build` was changed to the directory of the Manifest + file. This now allows cargo to pick up `.cargo/config.toml` files located in the source tree. + ([205](https://github.com/corrosion-rs/corrosion/pull/205)) +- Corrosion internally invokes `cargo build`. When passing arguments to `cargo build`, Corrosion + now uses the CMake `VERBATIM` option. In rare cases this may require you to change how you quote + parameters passed to corrosion (e.g. via `corrosion_add_target_rustflags()`). + For example setting a `cfg` option previously required double escaping the rustflag like this + `"--cfg=something=\\\"value\\\""`, but now it can be passed to corrosion without any escapes: + `--cfg=something="value"`. +- Corrosion now respects the CMake `OUTPUT_DIRECTORY` target properties. More details in the "New features" section. + +## New features + +- Support setting rustflags for only the main target and none of its dependencies ([215](https://github.com/corrosion-rs/corrosion/pull/215)). + A new function `corrosion_add_target_local_rustflags(target_name rustc_flag [more_flags ...])` + is added for this purpose. + This is useful in cases where you only need rustflags on the main-crate, but need to set different + flags for different targets. Without "local" Rustflags this would require rebuilds of the + dependencies when switching targets. +- Support explicitly selecting a linker ([208](https://github.com/corrosion-rs/corrosion/pull/208)). + The linker can be selected via `corrosion_set_linker(target_name linker)`. + Please note that this only has an effect for targets, where the final linker invocation is done + by cargo, i.e. targets where foreign code is linked into rust code and not the other way around. +- Corrosion now respects the CMake `OUTPUT_DIRECTORY` target properties and copies build artifacts to the expected + locations ([217](https://github.com/corrosion-rs/corrosion/pull/217)), if the properties are set. + This feature requires at least CMake 3.19 and is enabled by default if supported. Please note that the `OUTPUT_NAME` + target properties are currently not supported. + Specifically, the following target properties are now respected: + - [ARCHIVE_OUTPUT_DIRECTORY](https://cmake.org/cmake/help/latest/prop_tgt/ARCHIVE_OUTPUT_DIRECTORY.html) + - [LIBRARY_OUTPUT_DIRECTORY](https://cmake.org/cmake/help/latest/prop_tgt/LIBRARY_OUTPUT_DIRECTORY.html) + - [RUNTIME_OUTPUT_DIRECTORY](https://cmake.org/cmake/help/latest/prop_tgt/RUNTIME_OUTPUT_DIRECTORY.html) + - [PDB_OUTPUT_DIRECTORY](https://cmake.org/cmake/help/latest/prop_tgt/PDB_OUTPUT_DIRECTORY.html) +- Corrosion now supports packages with potentially multiple binaries (bins) and a library (lib) at the + same time. The only requirement is that the names of all `bin`s and `lib`s in the whole project must be unique. + Users can set the names in the `Cargo.toml` by adding `name = ` in the `[[bin]]` and `[lib]` tables. +- FindRust now has improved support for the `VERSION` option of `find_package` and will now attempt to find a matching + toolchain version. Previously it was only checked if the default toolchain matched to required version. +- For rustup managed toolchains a CMake error is issued with a helpful message if the required target for + the selected toolchain is not installed. + +## Fixes + +- Fix a CMake developer Warning when a Multi-Config Generator and Rust executable targets + ([#213](https://github.com/corrosion-rs/corrosion/pull/213)). +- FindRust now respects the `QUIET` option to `find_package()` in most cases. + +## Deprecation notice + +- Support for the MSVC Generators with CMake toolchains before 3.20 is deprecated and will be removed in the next + release (v0.4). All other Multi-config Generators already require CMake 3.20. + +## Internal Changes + +- The CMake Generator written in Rust and `CorrosionGenerator.cmake` which are responsible for parsing + `cargo metadata` output to create corresponding CMake targets for all Rust targets now share most code. + This greatly simplified the CMake generator written in Rust and makes it much easier maintaining and adding + new features regardless of how `cargo metadata` is parsed. + +# 0.2.2 (2022-09-01) + +## Fixes + +- Do not use C++17 in the tests (makes tests work with older C++ compilers) ([184](https://github.com/corrosion-rs/corrosion/pull/184)) +- Fix finding cargo on NixOS ([192](https://github.com/corrosion-rs/corrosion/pull/192)) +- Fix issue with Rustflags test when using a Build type other than Debug and Release ([203](https://github.com/corrosion-rs/corrosion/pull/203)). + +# 0.2.1 (2022-05-07) + +## Fixes + +- Fix missing variables provided by corrosion, when corrosion is used as a subdirectory ([181](https://github.com/corrosion-rs/corrosion/pull/181)): + Public [Variables](https://github.com/corrosion-rs/corrosion#information-provided-by-corrosion) set + by Corrosion were not visible when using Corrosion as a subdirectory, due to the wrong scope of + the variables. This was fixed by promoting the respective variables to Cache variables. + +# 0.2.0 (2022-05-05) + +## Breaking changes + +- Removed the integrator build script ([#156](https://github.com/corrosion-rs/corrosion/pull/156)). + The build script provided by corrosion (for rust code that links in foreign code) is no longer necessary, + so users can just remove the dependency. + +## Deprecations + +- Direct usage of the following target properties has been deprecated. The names of the custom properties are + no longer considered part of the public API and may change in the future. Instead, please use the functions + provided by corrosion. Internally different property names are used depending on the CMake version. + - `CORROSION_FEATURES`, `CORROSION_ALL_FEATURES`, `CORROSION_NO_DEFAULT_FEATURES`. Instead please use + `corrosion_set_features()`. See the updated Readme for details. + - `CORROSION_ENVIRONMENT_VARIABLES`. Please use `corrosion_set_env_vars()` instead. + - `CORROSION_USE_HOST_BUILD`. Please use `corrosion_set_hostbuild()` instead. +- The Minimum CMake version will likely be increased for the next major release. At the very least we want to drop + support for CMake 3.12, but requiring CMake 3.16 or even 3.18 is also on the table. If you are using a CMake version + that would be no longer supported by corrosion, please comment on issue + [#168](https://github.com/corrosion-rs/corrosion/issues/168), so that we can gauge the number of affected users. + +## New features + +- Add `NO_STD` option to `corrosion_import_crate` ([#154](https://github.com/corrosion-rs/corrosion/pull/154)). +- Remove the requirement of building the Rust based generator crate for CMake >= 3.19. This makes using corrosion as + a subdirectory as fast as the installed version (since everything is done in CMake). + ([#131](https://github.com/corrosion-rs/corrosion/pull/131), [#161](https://github.com/corrosion-rs/corrosion/pull/161)) + If you do choose to install Corrosion, then by default the old Generator is still compiled and installed, so you can + fall back to using it in case you use multiple cmake versions on the same machine for different projects. + +## Fixes + +- Fix Corrosion on MacOS 11 and 12 ([#167](https://github.com/corrosion-rs/corrosion/pull/167) and + [#164](https://github.com/corrosion-rs/corrosion/pull/164)). +- Improve robustness of parsing the LLVM version (exported in `Rust_LLVM_VERSION`). It now also works for + Rust versions, where the LLVM version is reported as `MAJOR.MINOR`. ([#148](https://github.com/corrosion-rs/corrosion/pull/148)) +- Fix a bug which occurred when Corrosion was added multiple times via `add_subdirectory()` + ([#143](https://github.com/corrosion-rs/corrosion/pull/143)). +- Set `CC_` and `CXX_` environment variables for the invocation of + `cargo build` to the compilers selected by CMake (if any) + ([#138](https://github.com/corrosion-rs/corrosion/pull/138) and [#161](https://github.com/corrosion-rs/corrosion/pull/161)). + This should ensure that C dependencies built in cargo buildscripts via [cc-rs](https://github.com/alexcrichton/cc-rs) + use the same compiler as CMake built dependencies. Users can override the compiler by specifying the higher + priority environment variable variants with dashes instead of underscores (See cc-rs documentation for details). +- Fix Ninja-Multiconfig Generator support for CMake versions >= 3.20. Previous CMake versions are missing a feature, + which prevents us from supporting the Ninja-Multiconfig generator. ([#137](https://github.com/corrosion-rs/corrosion/pull/137)) + + +# 0.1.0 (2022-02-01) + +This is the first release of corrosion after it was moved to the new corrosion-rs organization. +Since there are no previous releases, this is not a complete changelog but only lists changes since +September 2021. + +## New features +- [Add --profile support for rust >= 1.57](https://github.com/corrosion-rs/corrosion/pull/130): + Allows users to specify a custom cargo profile with + `corrosion_import_crate(... PROFILE )`. +- [Add support for specifying per-target Rustflags](https://github.com/corrosion-rs/corrosion/pull/127): + Rustflags can be added via `corrosion_add_target_rustflags( [rustflags1...])` +- [Add `Rust_IS_NIGHTLY` and `Rust_LLVM_VERSION` variables](https://github.com/corrosion-rs/corrosion/pull/123): + This may be useful if you want to conditionally enabled features when using a nightly toolchain + or a specific LLVM Version. +- [Let `FindRust` fail gracefully if rustc is not found](https://github.com/corrosion-rs/corrosion/pull/111): + This allows using `FindRust` in a more general setting (without corrosion). +- [Add support for cargo feature selection](https://github.com/corrosion-rs/corrosion/pull/108): + See the [README](https://github.com/corrosion-rs/corrosion#cargo-feature-selection) for details on + how to select features. + + +## Fixes +- [Fix the cargo-clean target](https://github.com/corrosion-rs/corrosion/pull/129) +- [Fix #84: CorrosionConfig.cmake looks in wrong place for Corrosion::Generator when CMAKE_INSTALL_LIBEXEC is an absolute path](https://github.com/corrosion-rs/corrosion/pull/122/commits/6f29af3ac53917ca2e0638378371e715a18a532d) +- [Fix #116: (Option CORROSION_INSTALL_EXECUTABLE not working)](https://github.com/corrosion-rs/corrosion/commit/97d44018fac1b1a2a7c095288c628f5bbd9b3184) +- [Fix building on Windows with rust >= 1.57](https://github.com/corrosion-rs/corrosion/pull/120) + +## Known issues: +- Corrosion is currently not working on macos-11 and newer. See issue [#104](https://github.com/corrosion-rs/corrosion/issues/104). + Contributions are welcome. diff -r 2b4f361e3891 -r 629d5123a979 tools/corrosion/cmake/Corrosion.cmake --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/corrosion/cmake/Corrosion.cmake Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,2052 @@ +cmake_minimum_required(VERSION 3.22) + +list(APPEND CMAKE_MESSAGE_CONTEXT "Corrosion") + +message(DEBUG "Using Corrosion ${Corrosion_VERSION} with CMake ${CMAKE_VERSION} " + "and the `${CMAKE_GENERATOR}` Generator" +) + +get_cmake_property(COR_IS_MULTI_CONFIG GENERATOR_IS_MULTI_CONFIG) +set(COR_IS_MULTI_CONFIG "${COR_IS_MULTI_CONFIG}" CACHE BOOL "Do not change this" FORCE) +mark_as_advanced(FORCE COR_IS_MULTI_CONFIG) + + +if(NOT COR_IS_MULTI_CONFIG AND DEFINED CMAKE_CONFIGURATION_TYPES) + message(WARNING "The Generator is ${CMAKE_GENERATOR}, which is not a multi-config " + "Generator, but CMAKE_CONFIGURATION_TYPES is set. Please don't set " + "CMAKE_CONFIGURATION_TYPES unless you are using a multi-config Generator." + ) +endif() + +option(CORROSION_VERBOSE_OUTPUT "Enables verbose output from Corrosion and Cargo" OFF) + +if(DEFINED CORROSION_RESPECT_OUTPUT_DIRECTORY AND NOT CORROSION_RESPECT_OUTPUT_DIRECTORY) + message(WARNING "The option CORROSION_RESPECT_OUTPUT_DIRECTORY was removed." + " Corrosion now always attempts to respect the output directory.") +endif() + +option( + CORROSION_NO_WARN_PARSE_TARGET_TRIPLE_FAILED + "Surpresses a warning if the parsing the target triple failed." + OFF +) + +find_package(Rust REQUIRED) + +if(CMAKE_GENERATOR MATCHES "Visual Studio" + AND (NOT CMAKE_VS_PLATFORM_NAME STREQUAL CMAKE_VS_PLATFORM_NAME_DEFAULT) + AND Rust_VERSION VERSION_LESS "1.54") + message(FATAL_ERROR "Due to a cargo issue, cross-compiling with a Visual Studio generator and rust versions" + " before 1.54 is not supported. Rust build scripts would be linked with the cross-compiler linker, which" + " causes the build to fail. Please upgrade your Rust version to 1.54 or newer.") +endif() + +# message(STATUS "Using Corrosion as a subdirectory") + +get_property( + RUSTC_EXECUTABLE + TARGET Rust::Rustc PROPERTY IMPORTED_LOCATION +) + +get_property( + CARGO_EXECUTABLE + TARGET Rust::Cargo PROPERTY IMPORTED_LOCATION +) + +function(_corrosion_bin_target_suffix target_name out_var_suffix) + get_target_property(hostbuild "${target_name}" ${_CORR_PROP_HOST_BUILD}) + if((hostbuild AND CMAKE_HOST_WIN32) + OR ((NOT hostbuild) AND (Rust_CARGO_TARGET_OS STREQUAL "windows"))) + set(_suffix ".exe") + elseif(Rust_CARGO_TARGET_OS STREQUAL "vxworks") + set(_suffix ".vxe") + else() + set(_suffix "") + endif() + set(${out_var_suffix} "${_suffix}" PARENT_SCOPE) +endfunction() + +# Do not call this function directly! +# +# This function should be called deferred to evaluate target properties late in the configure stage. +# IMPORTED_LOCATION does not support Generator expressions, so we must evaluate the output +# directory target property value at configure time. This function must be deferred to the end of +# the configure stage, so we can be sure that the output directory is not modified afterwards. +function(_corrosion_set_imported_location_deferred target_name base_property output_directory_property filename) + # The output directory property is expected to be set on the exposed target (without postfix), + # but we need to set the imported location on the actual library target with postfix. + if("${target_name}" MATCHES "^(.+)-(static|shared)$") + set(output_dir_prop_target_name "${CMAKE_MATCH_1}") + else() + set(output_dir_prop_target_name "${target_name}") + endif() + + # Append .exe suffix for executable by-products if the target is windows or if it's a host + # build and the host is Windows. + get_target_property(target_type ${target_name} TYPE) + if(${target_type} STREQUAL "EXECUTABLE" AND (NOT "${filename}" MATCHES "\.pdb$")) + _corrosion_bin_target_suffix(${target_name} "suffix") + string(APPEND filename "${suffix}") + endif() + + get_target_property(output_directory "${output_dir_prop_target_name}" "${output_directory_property}") + message(DEBUG "Output directory property (target ${output_dir_prop_target_name}): ${output_directory_property} dir: ${output_directory}") + + foreach(config_type ${CMAKE_CONFIGURATION_TYPES}) + string(TOUPPER "${config_type}" config_type_upper) + get_target_property(output_dir_curr_config ${output_dir_prop_target_name} + "${output_directory_property}_${config_type_upper}" + ) + if(output_dir_curr_config) + set(curr_out_dir "${output_dir_curr_config}") + elseif(output_directory) + set(curr_out_dir "${output_directory}") + else() + set(curr_out_dir "${CMAKE_CURRENT_BINARY_DIR}") + endif() + string(REPLACE "\$" "${config_type}" curr_out_dir "${curr_out_dir}") + message(DEBUG "Setting ${base_property}_${config_type_upper} for target ${target_name}" + " to `${curr_out_dir}/${filename}`.") + + string(GENEX_STRIP "${curr_out_dir}" stripped_out_dir) + if(NOT ("${stripped_out_dir}" STREQUAL "${curr_out_dir}")) + message(FATAL_ERROR "${output_directory_property} for target ${output_dir_prop_target_name} " + "contained an unexpected Generator expression. Output dir: `${curr_out_dir}`" + "Note: Corrosion only supports the `\$` generator expression for output directories.") + endif() + + # For Multiconfig we want to specify the correct location for each configuration + set_property( + TARGET ${target_name} + PROPERTY "${base_property}_${config_type_upper}" + "${curr_out_dir}/${filename}" + ) + set(base_output_directory "${curr_out_dir}") + endforeach() + + if(NOT COR_IS_MULTI_CONFIG) + if(output_directory) + set(base_output_directory "${output_directory}") + else() + set(base_output_directory "${CMAKE_CURRENT_BINARY_DIR}") + endif() + string(REPLACE "\$" "${CMAKE_BUILD_TYPE}" base_output_directory "${base_output_directory}") + string(GENEX_STRIP "${base_output_directory}" stripped_out_dir) + if(NOT ("${stripped_out_dir}" STREQUAL "${base_output_directory}")) + message(FATAL_ERROR "${output_dir_prop_target_name} for target ${output_dir_prop_target_name} " + "contained an unexpected Generator expression. Output dir: `${base_output_directory}`" + "Note: Corrosion only supports the `\$` generator expression for output directories.") + endif() + endif() + + message(DEBUG "Setting ${base_property} for target ${target_name}" + " to `${base_output_directory}/${filename}`.") + + # IMPORTED_LOCATION must be set regardless of possible overrides. In the multiconfig case, + # the last configuration "wins" (IMPORTED_LOCATION is not documented to have Genex support). + set_property( + TARGET ${target_name} + PROPERTY "${base_property}" "${base_output_directory}/${filename}" + ) +endfunction() + +# Set the imported location of a Rust target. +# +# Rust targets are built via custom targets / custom commands. The actual artifacts are exposed +# to CMake as imported libraries / executables that depend on the cargo_build command. For CMake +# to find the built artifact we need to set the IMPORTED location to the actual location on disk. +# Corrosion tries to copy the artifacts built by cargo to standard locations. The IMPORTED_LOCATION +# is set to point to the copy, and not the original from the cargo build directory. +# +# Parameters: +# - target_name: Name of the Rust target +# - base_property: Name of the base property - i.e. `IMPORTED_LOCATION` or `IMPORTED_IMPLIB`. +# - output_directory_property: Target property name that determines the standard location for the +# artifact. +# - filename of the artifact. +function(_corrosion_set_imported_location target_name base_property output_directory_property filename) + cmake_language(EVAL CODE " + cmake_language(DEFER + CALL + _corrosion_set_imported_location_deferred + [[${target_name}]] + [[${base_property}]] + [[${output_directory_property}]] + [[${filename}]] + ) + ") +endfunction() + +function(_corrosion_copy_byproduct_deferred target_name output_dir_prop_names cargo_build_dir file_names) + if(ARGN) + message(FATAL_ERROR "Unexpected additional arguments") + endif() + + foreach(output_dir_prop_name ${output_dir_prop_names}) + get_target_property(output_dir ${target_name} "${output_dir_prop_name}") + if(output_dir) + break() + endif() + endforeach() + + # A Genex expanding to the output directory depending on the configuration. + set(multiconfig_out_dir_genex "") + + foreach(config_type ${CMAKE_CONFIGURATION_TYPES}) + string(TOUPPER "${config_type}" config_type_upper) + foreach(output_dir_prop_name ${output_dir_prop_names}) + get_target_property(output_dir_curr_config ${target_name} "${output_dir_prop_name}_${config_type_upper}") + if(output_dir_curr_config) + break() + endif() + endforeach() + + if(output_dir_curr_config) + set(curr_out_dir "${output_dir_curr_config}") + elseif(output_dir) + # Fallback to `output_dir` if specified + # Note: Multi-configuration generators append a per-configuration subdirectory to the + # specified directory unless a generator expression is used (from CMake documentation). + set(curr_out_dir "${output_dir}") + else() + # Fallback to the default directory. We do not append the configuration directory here + # and instead let CMake do this, since otherwise the resolving of dynamic library + # imported paths may fail. + set(curr_out_dir "${CMAKE_CURRENT_BINARY_DIR}") + endif() + set(multiconfig_out_dir_genex "${multiconfig_out_dir_genex}$<$:${curr_out_dir}>") + endforeach() + + if(COR_IS_MULTI_CONFIG) + set(output_dir "${multiconfig_out_dir_genex}") + else() + if(NOT output_dir) + # Fallback to default directory. + set(output_dir "${CMAKE_CURRENT_BINARY_DIR}") + endif() + endif() + + # Append .exe suffix for executable by-products if the target is windows or if it's a host + # build and the host is Windows. + get_target_property(target_type "${target_name}" TYPE) + if (target_type STREQUAL "EXECUTABLE") + list(LENGTH file_names list_len) + if(NOT list_len EQUAL "1") + message(FATAL_ERROR + "Internal error: Exactly one filename should be passed for executable types.") + endif() + _corrosion_bin_target_suffix(${target_name} "suffix") + if(suffix AND (NOT "${file_names}" MATCHES "\.pdb$")) + # For executable targets we know / checked that only one file will be passed. + string(APPEND file_names "${suffix}") + endif() + endif() + set(src_file_names "${file_names}") + if(Rust_CARGO_TARGET_ENV STREQUAL "gnullvm") + # Workaround for cargo not exposing implibs yet. + list(TRANSFORM src_file_names PREPEND "deps/" REGEX "\.dll\.a$") + endif() + list(TRANSFORM src_file_names PREPEND "${cargo_build_dir}/") + list(TRANSFORM file_names PREPEND "${output_dir}/" OUTPUT_VARIABLE dst_file_names) + message(DEBUG "Adding command to copy byproducts `${file_names}` to ${dst_file_names}") + add_custom_command(TARGET _cargo-build_${target_name} + POST_BUILD + # output_dir may contain a Generator expression. + COMMAND ${CMAKE_COMMAND} -E make_directory "${output_dir}" + COMMAND + ${CMAKE_COMMAND} -E copy_if_different + # tested to work with both multiple files and paths with spaces + ${src_file_names} + "${output_dir}" + BYPRODUCTS ${dst_file_names} + COMMENT "Copying byproducts `${file_names}` to ${output_dir}" + VERBATIM + COMMAND_EXPAND_LISTS + ) +endfunction() + +# Copy the artifacts generated by cargo to the appropriate destination. +# +# Parameters: +# - target_name: The name of the Rust target +# - output_dir_prop_names: The property name(s) controlling the destination (e.g. +# `LIBRARY_OUTPUT_DIRECTORY` or `PDB_OUTPUT_DIRECTORY;RUNTIME_OUTPUT_DIRECTORY`) +# - cargo_build_dir: the directory cargo build places it's output artifacts in. +# - filenames: the file names of any output artifacts as a list. +function(_corrosion_copy_byproducts target_name output_dir_prop_names cargo_build_dir file_names) + cmake_language(EVAL CODE " + cmake_language(DEFER + CALL + _corrosion_copy_byproduct_deferred + [[${target_name}]] + [[${output_dir_prop_names}]] + [[${cargo_build_dir}]] + [[${file_names}]] + ) + ") +endfunction() + + +# Add targets for the static and/or shared libraries of the rust target. +# The generated byproduct names are returned via the `OUT__BYPRODUCTS` arguments. +function(_corrosion_add_library_target) + set(OPTIONS "") + set(ONE_VALUE_KEYWORDS + WORKSPACE_MANIFEST_PATH + TARGET_NAME + OUT_ARCHIVE_OUTPUT_BYPRODUCTS + OUT_SHARED_LIB_BYPRODUCTS + OUT_PDB_BYPRODUCT + ) + set(MULTI_VALUE_KEYWORDS LIB_KINDS) + cmake_parse_arguments(PARSE_ARGV 0 CALT "${OPTIONS}" "${ONE_VALUE_KEYWORDS}" "${MULTI_VALUE_KEYWORDS}") + + if(DEFINED CALT_UNPARSED_ARGUMENTS) + message(FATAL_ERROR "Internal error - unexpected arguments: ${CALT_UNPARSED_ARGUMENTS}") + elseif(DEFINED CALT_KEYWORDS_MISSING_VALUES) + message(FATAL_ERROR "Internal error - the following keywords had no associated value(s):" + "${CALT_KEYWORDS_MISSING_VALUES}") + endif() + list(TRANSFORM ONE_VALUE_KEYWORDS PREPEND CALT_ OUTPUT_VARIABLE required_arguments) + foreach(required_argument ${required_arguments} ) + if(NOT DEFINED "${required_argument}") + message(FATAL_ERROR "Internal error: Missing required argument ${required_argument}." + "Complete argument list: ${ARGN}" + ) + endif() + endforeach() + if("staticlib" IN_LIST CALT_LIB_KINDS) + set(has_staticlib TRUE) + endif() + if("cdylib" IN_LIST CALT_LIB_KINDS) + set(has_cdylib TRUE) + endif() + + if(NOT (has_staticlib OR has_cdylib)) + message(FATAL_ERROR "Unknown library type(s): ${CALT_LIB_KINDS}") + endif() + set(workspace_manifest_path "${CALT_WORKSPACE_MANIFEST_PATH}") + set(target_name "${CALT_TARGET_NAME}") + + set(is_windows "") + set(is_windows_gnu "") + set(is_windows_msvc "") + set(is_macos "") + if(Rust_CARGO_TARGET_OS STREQUAL "windows") + set(is_windows TRUE) + if(Rust_CARGO_TARGET_ENV STREQUAL "msvc") + set(is_windows_msvc TRUE) + elseif(Rust_CARGO_TARGET_ENV STREQUAL "gnu" OR Rust_CARGO_TARGET_ENV STREQUAL "gnullvm") + set(is_windows_gnu TRUE) + endif() + elseif(Rust_CARGO_TARGET_OS STREQUAL "darwin") + set(is_macos TRUE) + endif() + + # target file names + string(REPLACE "-" "_" lib_name "${target_name}") + + if(is_windows_msvc) + set(static_lib_name "${lib_name}.lib") + else() + set(static_lib_name "lib${lib_name}.a") + endif() + + if(is_windows) + set(dynamic_lib_name "${lib_name}.dll") + elseif(is_macos) + set(dynamic_lib_name "lib${lib_name}.dylib") + else() + set(dynamic_lib_name "lib${lib_name}.so") + endif() + + if(is_windows_msvc) + set(implib_name "${lib_name}.dll.lib") + elseif(is_windows_gnu) + set(implib_name "lib${lib_name}.dll.a") + elseif(is_windows) + message(FATAL_ERROR "Unknown windows environment - Can't determine implib name") + endif() + + + set(pdb_name "${lib_name}.pdb") + + set(archive_output_byproducts "") + if(has_staticlib) + list(APPEND archive_output_byproducts ${static_lib_name}) + endif() + + if(has_cdylib) + set("${CALT_OUT_SHARED_LIB_BYPRODUCTS}" "${dynamic_lib_name}" PARENT_SCOPE) + if(is_windows) + list(APPEND archive_output_byproducts ${implib_name}) + endif() + if(is_windows_msvc) + set("${CALT_OUT_PDB_BYPRODUCT}" "${pdb_name}" PARENT_SCOPE) + endif() + endif() + set("${CALT_OUT_ARCHIVE_OUTPUT_BYPRODUCTS}" "${archive_output_byproducts}" PARENT_SCOPE) + + if(has_staticlib) + add_library(${target_name}-static STATIC IMPORTED GLOBAL) + add_dependencies(${target_name}-static cargo-build_${target_name}) + set_target_properties(${target_name}-static PROPERTIES COR_FILE_NAME ${static_lib_name}) + + _corrosion_set_imported_location("${target_name}-static" "IMPORTED_LOCATION" + "ARCHIVE_OUTPUT_DIRECTORY" + "${static_lib_name}") + + # Todo: NO_STD target property? + if(NOT COR_NO_STD) + set_property( + TARGET ${target_name}-static + PROPERTY INTERFACE_LINK_LIBRARIES ${Rust_CARGO_TARGET_LINK_NATIVE_LIBS} + ) + set_property( + TARGET ${target_name}-static + PROPERTY INTERFACE_LINK_OPTIONS ${Rust_CARGO_TARGET_LINK_OPTIONS} + ) + if(is_macos) + set_property(TARGET ${target_name}-static + PROPERTY INTERFACE_LINK_DIRECTORIES "/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/lib" + ) + endif() + endif() + endif() + + if(has_cdylib) + add_library(${target_name}-shared SHARED IMPORTED GLOBAL) + add_dependencies(${target_name}-shared cargo-build_${target_name}) + set_target_properties(${target_name}-shared PROPERTIES COR_FILE_NAME ${dynamic_lib_name}) + + # Todo: (Not new issue): What about IMPORTED_SONAME and IMPORTED_NO_SYSTEM? + _corrosion_set_imported_location("${target_name}-shared" "IMPORTED_LOCATION" + "LIBRARY_OUTPUT_DIRECTORY" + "${dynamic_lib_name}" + ) + # In the future we would probably prefer to let Rust set the soname for packages >= 1.0. + # This is tracked in issue #333. + set_target_properties(${target_name}-shared PROPERTIES IMPORTED_NO_SONAME TRUE) + + if(is_windows) + _corrosion_set_imported_location("${target_name}-shared" "IMPORTED_IMPLIB" + "ARCHIVE_OUTPUT_DIRECTORY" + "${implib_name}" + ) + set_target_properties(${target_name}-shared PROPERTIES COR_IMPLIB_FILE_NAME ${implib_name}) + endif() + + if(is_macos) + set_property(TARGET ${target_name}-shared + PROPERTY INTERFACE_LINK_DIRECTORIES "/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/lib" + ) + endif() + endif() + + if(has_cdylib AND has_staticlib) + if(BUILD_SHARED_LIBS) + target_link_libraries(${target_name} INTERFACE ${target_name}-shared) + else() + target_link_libraries(${target_name} INTERFACE ${target_name}-static) + endif() + elseif(has_cdylib) + target_link_libraries(${target_name} INTERFACE ${target_name}-shared) + else() + target_link_libraries(${target_name} INTERFACE ${target_name}-static) + endif() +endfunction() + +function(_corrosion_add_bin_target workspace_manifest_path bin_name out_bin_byproduct out_pdb_byproduct) + if(NOT bin_name) + message(FATAL_ERROR "No bin_name in _corrosion_add_bin_target for target ${target_name}") + endif() + + string(REPLACE "-" "_" bin_name_underscore "${bin_name}") + + set(pdb_name "${bin_name_underscore}.pdb") + + if(Rust_CARGO_TARGET_ENV STREQUAL "msvc") + set(${out_pdb_byproduct} "${pdb_name}" PARENT_SCOPE) + endif() + + # Potential .exe suffix will be added later, also depending on possible hostbuild + # target property + set(bin_filename "${bin_name}") + set(${out_bin_byproduct} "${bin_filename}" PARENT_SCOPE) + add_dependencies(${bin_name} cargo-build_${bin_name}) + + if(Rust_CARGO_TARGET_OS STREQUAL "darwin") + set_property(TARGET ${bin_name} + PROPERTY INTERFACE_LINK_DIRECTORIES "/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/lib" + ) + endif() + + _corrosion_set_imported_location("${bin_name}" "IMPORTED_LOCATION" + "RUNTIME_OUTPUT_DIRECTORY" + "${bin_filename}" + ) + +endfunction() + + +include(CorrosionGenerator) + +# Note: `cmake_language(GET_MESSAGE_LOG_LEVEL )` requires CMake 3.25, +# so we offer our own option to control verbosity of downstream commands (e.g. cargo build) +if (CORROSION_VERBOSE_OUTPUT) + set(_CORROSION_VERBOSE_OUTPUT_FLAG --verbose CACHE INTERNAL "") +else() + # We want to silence some less important commands by default. + set(_CORROSION_QUIET_OUTPUT_FLAG --quiet CACHE INTERNAL "") +endif() + +set(_CORROSION_CARGO_VERSION ${Rust_CARGO_VERSION} CACHE INTERNAL "cargo version used by corrosion") +set(_CORROSION_RUST_CARGO_TARGET ${Rust_CARGO_TARGET} CACHE INTERNAL "target triple used by corrosion") +set(_CORROSION_RUST_CARGO_HOST_TARGET ${Rust_CARGO_HOST_TARGET} CACHE INTERNAL "host triple used by corrosion") +set(_CORROSION_RUSTC "${RUSTC_EXECUTABLE}" CACHE INTERNAL "Path to rustc used by corrosion") +set(_CORROSION_CARGO "${CARGO_EXECUTABLE}" CACHE INTERNAL "Path to cargo used by corrosion") + +string(REPLACE "-" "_" _CORROSION_RUST_CARGO_TARGET_UNDERSCORE "${Rust_CARGO_TARGET}") +set(_CORROSION_RUST_CARGO_TARGET_UNDERSCORE "${_CORROSION_RUST_CARGO_TARGET_UNDERSCORE}" CACHE INTERNAL "lowercase target triple with underscores") +string(TOUPPER "${_CORROSION_RUST_CARGO_TARGET_UNDERSCORE}" _CORROSION_TARGET_TRIPLE_UPPER) +set(_CORROSION_RUST_CARGO_TARGET_UPPER + "${_CORROSION_TARGET_TRIPLE_UPPER}" + CACHE INTERNAL + "target triple in uppercase with underscore" +) + +# We previously specified some Custom properties as part of our public API, however the chosen names prevented us from +# supporting CMake versions before 3.19. In order to both support older CMake versions and not break existing code +# immediately, we are using a different property name depending on the CMake version. However users avoid using +# any of the properties directly, as they are no longer part of the public API and are to be considered deprecated. +# Instead use the corrosion_set_... functions as documented in the Readme. +set(_CORR_PROP_FEATURES CORROSION_FEATURES CACHE INTERNAL "") +set(_CORR_PROP_ALL_FEATURES CORROSION_ALL_FEATURES CACHE INTERNAL "") +set(_CORR_PROP_NO_DEFAULT_FEATURES CORROSION_NO_DEFAULT_FEATURES CACHE INTERNAL "") +set(_CORR_PROP_ENV_VARS CORROSION_ENVIRONMENT_VARIABLES CACHE INTERNAL "") +set(_CORR_PROP_HOST_BUILD CORROSION_USE_HOST_BUILD CACHE INTERNAL "") + +# Add custom command to build one target in a package (crate) +# +# A target may be either a specific bin +function(_add_cargo_build out_cargo_build_out_dir) + set(options NO_LINKER_OVERRIDE) + set(one_value_args PACKAGE TARGET MANIFEST_PATH WORKSPACE_MANIFEST_PATH) + set(multi_value_args BYPRODUCTS TARGET_KINDS) + cmake_parse_arguments( + ACB + "${options}" + "${one_value_args}" + "${multi_value_args}" + ${ARGN} + ) + + if(DEFINED ACB_UNPARSED_ARGUMENTS) + message(FATAL_ERROR "Internal error - unexpected arguments: " + ${ACB_UNPARSED_ARGUMENTS}) + elseif(DEFINED ACB_KEYWORDS_MISSING_VALUES) + message(FATAL_ERROR "Internal error - missing values for the following arguments: " + ${ACB_KEYWORDS_MISSING_VALUES}) + endif() + + set(package_name "${ACB_PACKAGE}") + set(target_name "${ACB_TARGET}") + set(path_to_toml "${ACB_MANIFEST_PATH}") + set(target_kinds "${ACB_TARGET_KINDS}") + set(workspace_manifest_path "${ACB_WORKSPACE_MANIFEST_PATH}") + set(build_byproducts "${ACB_BYPRODUCTS}") + + unset(cargo_rustc_crate_types) + if(NOT target_kinds) + message(FATAL_ERROR "TARGET_KINDS not specified") + elseif("staticlib" IN_LIST target_kinds OR "cdylib" IN_LIST target_kinds) + set(cargo_rustc_filter "--lib") + if("${Rust_VERSION}" VERSION_GREATER_EQUAL "1.64") + # https://doc.rust-lang.org/1.64.0/cargo/commands/cargo-rustc.html + # `--crate-type` is documented since Rust 1.64 for `cargo rustc`. + # We just unconditionally set it when available, to support overriding the crate type. + # Due to https://github.com/rust-lang/cargo/issues/14498 we can't use one argument and pass a + # comma seperated list. Instead we use multiple arguments. + set(cargo_rustc_crate_types "${target_kinds}") + list(TRANSFORM cargo_rustc_crate_types PREPEND "--crate-type=") + endif() + elseif("bin" IN_LIST target_kinds) + set(cargo_rustc_filter "--bin=${target_name}") + else() + message(FATAL_ERROR "TARGET_KINDS contained unknown kind `${target_kind}`") + endif() + + if (NOT IS_ABSOLUTE "${path_to_toml}") + set(path_to_toml "${CMAKE_SOURCE_DIR}/${path_to_toml}") + endif() + get_filename_component(workspace_toml_dir ${path_to_toml} DIRECTORY ) + + if (CMAKE_VS_PLATFORM_NAME) + set (build_dir "${CMAKE_VS_PLATFORM_NAME}/$") + elseif(COR_IS_MULTI_CONFIG) + set (build_dir "$") + else() + set (build_dir .) + endif() + + # If a CMake sysroot is specified, forward it to the linker rustc invokes, too. CMAKE_SYSROOT is documented + # to be passed via --sysroot, so we assume that when it's set, the linker supports this option in that style. + if(CMAKE_CROSSCOMPILING AND CMAKE_SYSROOT) + set(corrosion_link_args "--sysroot=${CMAKE_SYSROOT}") + endif() + + if(COR_ALL_FEATURES) + set(all_features_arg --all-features) + endif() + if(COR_NO_DEFAULT_FEATURES) + set(no_default_features_arg --no-default-features) + endif() + + set(global_rustflags_target_property "$>") + set(local_rustflags_target_property "$>") + + # todo: this probably should be TARGET_GENEX_EVAL + set(features_target_property "$>") + set(features_genex "$<$:--features=$>>") + + # target property overrides corrosion_import_crate argument + set(all_features_target_property "$>") + set(all_features_arg "$<$:--all-features>") + + set(no_default_features_target_property "$>") + set(no_default_features_arg "$<$:--no-default-features>") + + set(build_env_variable_genex "$>") + set(hostbuild_override "$>") + set(if_not_host_build_condition "$") + + set(corrosion_link_args "$<${if_not_host_build_condition}:${corrosion_link_args}>") + # We always set `--target`, so that cargo always places artifacts into a directory with the + # target triple. + set(cargo_target_option "--target=$") + + # The target may be a filepath to custom target json file. For host targets we assume that they are built-in targets. + _corrosion_strip_target_triple(${_CORROSION_RUST_CARGO_TARGET} stripped_target_triple) + set(target_artifact_dir "$") + + set(flags_genex "$>") + + set(explicit_linker_property "$") + set(explicit_linker_defined "$") + + set(cargo_profile_target_property "$>") + + # Option to override the rustc/cargo binary to something other than the global default + set(rustc_override "$") + set(cargo_override "$") + set(rustc_bin "$,${rustc_override},${_CORROSION_RUSTC}>") + set(cargo_bin "$,${cargo_override},${_CORROSION_CARGO}>") + + + # Rust will add `-lSystem` as a flag for the linker on macOS. Adding the -L flag via RUSTFLAGS only fixes the + # problem partially - buildscripts still break, since they won't receive the RUSTFLAGS. This seems to only be a + # problem if we specify the linker ourselves (which we do, since this is necessary for e.g. linking C++ code). + # We can however set `LIBRARY_PATH`, which is propagated to the build-script-build properly. + if(NOT CMAKE_CROSSCOMPILING AND CMAKE_SYSTEM_NAME STREQUAL "Darwin") + # not needed anymore on macos 13 (and causes issues) + if(${CMAKE_SYSTEM_VERSION} VERSION_LESS 22) + set(cargo_library_path "LIBRARY_PATH=/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/lib") + endif() + elseif(CMAKE_CROSSCOMPILING AND CMAKE_HOST_SYSTEM_NAME STREQUAL "Darwin") + if(${CMAKE_HOST_SYSTEM_VERSION} VERSION_LESS 22) + set(cargo_library_path "$<${hostbuild_override}:LIBRARY_PATH=/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/lib>") + endif() + endif() + + set(cargo_profile_set "$") + # In the default case just specify --release or nothing to stay compatible with + # older rust versions. + set(default_profile_option "$<$,$>>:--release>") + # evaluates to either `--profile=`, `--release` or nothing (for debug). + set(cargo_profile "$") + + # If the profile name is `dev` change the dir name to `debug`. + set(is_dev_profile "$") + set(profile_dir_override "$<${is_dev_profile}:debug>") + set(profile_dir_is_overridden "$") + set(custom_profile_build_type_dir "$") + + set(default_build_type_dir "$,$>,debug,release>") + set(build_type_dir "$") + + set(cargo_target_dir "${CMAKE_BINARY_DIR}/${build_dir}/cargo/build") + set(cargo_build_dir "${cargo_target_dir}/${target_artifact_dir}/${build_type_dir}") + set("${out_cargo_build_out_dir}" "${cargo_build_dir}" PARENT_SCOPE) + + set(corrosion_cc_rs_flags) + + if(CMAKE_C_COMPILER) + # This variable is read by cc-rs (often used in build scripts) to determine the c-compiler. + # It can still be overridden if the user sets the non underscore variant via the environment variables + # on the target. + list(APPEND corrosion_cc_rs_flags "CC_${_CORROSION_RUST_CARGO_TARGET_UNDERSCORE}=${CMAKE_C_COMPILER}") + endif() + if(CMAKE_CXX_COMPILER) + list(APPEND corrosion_cc_rs_flags "CXX_${_CORROSION_RUST_CARGO_TARGET_UNDERSCORE}=${CMAKE_CXX_COMPILER}") + endif() + # cc-rs doesn't seem to support `llvm-ar` (commandline syntax), wo we might as well just use + # the default AR. + if(CMAKE_AR AND NOT (Rust_CARGO_TARGET_ENV STREQUAL "msvc")) + list(APPEND corrosion_cc_rs_flags "AR_${_CORROSION_RUST_CARGO_TARGET_UNDERSCORE}=${CMAKE_AR}") + endif() + + # Since we instruct cc-rs to use the compiler found by CMake, it is likely one that requires also + # specifying the target sysroot to use. CMake's generator makes sure to pass --sysroot with + # CMAKE_OSX_SYSROOT. Fortunately the compilers Apple ships also respect the SDKROOT environment + # variable, which we can set for use when cc-rs invokes the compiler. + if(CMAKE_HOST_SYSTEM_NAME STREQUAL "Darwin" AND CMAKE_OSX_SYSROOT) + list(APPEND corrosion_cc_rs_flags "SDKROOT=${CMAKE_OSX_SYSROOT}") + endif() + + # Ensure that cc-rs targets same Apple platform version as the CMake build + if(CMAKE_SYSTEM_NAME STREQUAL "Darwin" AND CMAKE_OSX_DEPLOYMENT_TARGET) + list(APPEND corrosion_cc_rs_flags "MACOSX_DEPLOYMENT_TARGET=${CMAKE_OSX_DEPLOYMENT_TARGET}") + endif() + + corrosion_add_target_local_rustflags("${target_name}" "$<$:-Clink-args=${corrosion_link_args}>") + + # todo: this should probably also be guarded by if_not_host_build_condition. + if(COR_NO_STD) + corrosion_add_target_local_rustflags("${target_name}" "-Cdefault-linker-libraries=no") + else() + corrosion_add_target_local_rustflags("${target_name}" "-Cdefault-linker-libraries=yes") + endif() + + set(global_joined_rustflags "$") + set(global_rustflags_genex "$<$:RUSTFLAGS=${global_joined_rustflags}>") + set(local_rustflags_delimiter "$<$:-->") + set(local_rustflags_genex "$<$:${local_rustflags_target_property}>") + + set(deps_link_languages_prop "$") + set(deps_link_languages "$") + set(target_uses_cxx "$") + unset(default_linker) + # With the MSVC ABI rustc only supports directly invoking the linker - Invoking cl as the linker driver is not supported. + if(NOT (Rust_CARGO_TARGET_ENV STREQUAL "msvc" OR COR_NO_LINKER_OVERRIDE)) + set(default_linker "$,${CMAKE_CXX_COMPILER},${CMAKE_C_COMPILER}>") + endif() + # Used to set a linker for a specific target-triple. + set(cargo_target_linker_var "CARGO_TARGET_${_CORROSION_RUST_CARGO_TARGET_UPPER}_LINKER") + set(linker "$") + set(cargo_target_linker $<$:${cargo_target_linker_var}=${linker}>) + + if(Rust_CROSSCOMPILING AND (CMAKE_C_COMPILER_TARGET OR CMAKE_CXX_COMPILER_TARGET)) + set(linker_target_triple "$,${CMAKE_CXX_COMPILER_TARGET},${CMAKE_C_COMPILER_TARGET}>") + set(rustflag_linker_arg "-Clink-args=--target=${linker_target_triple}") + set(rustflag_linker_arg "$<${if_not_host_build_condition}:${rustflag_linker_arg}>") + # Skip adding the linker argument, if the linker is explicitly set, since the + # explicit_linker_property will not be set when this function runs. + # Passing this rustflag is necessary for clang. + corrosion_add_target_local_rustflags("${target_name}" "$<$:${rustflag_linker_arg}>") + endif() + + message(DEBUG "TARGET ${target_name} produces byproducts ${build_byproducts}") + + add_custom_target( + _cargo-build_${target_name} + # Build crate + COMMAND + ${CMAKE_COMMAND} -E env + "${build_env_variable_genex}" + "${global_rustflags_genex}" + "${cargo_target_linker}" + "${corrosion_cc_rs_flags}" + "${cargo_library_path}" + "CORROSION_BUILD_DIR=${CMAKE_CURRENT_BINARY_DIR}" + "CARGO_BUILD_RUSTC=${rustc_bin}" + "${cargo_bin}" + rustc + ${cargo_rustc_filter} + ${cargo_target_option} + ${_CORROSION_VERBOSE_OUTPUT_FLAG} + ${all_features_arg} + ${no_default_features_arg} + ${features_genex} + --package ${package_name} + ${cargo_rustc_crate_types} + --manifest-path "${path_to_toml}" + --target-dir "${cargo_target_dir}" + ${cargo_profile} + ${flags_genex} + # Any arguments to cargo must be placed before this line + ${local_rustflags_delimiter} + ${local_rustflags_genex} + + # Note: `BYPRODUCTS` may not contain **target specific** generator expressions. + # This means we cannot use `${cargo_build_dir}`, since it currently uses `$` + # to determine the correct target directory, depending on if the hostbuild target property is + # set or not. + # BYPRODUCTS "${cargo_build_dir}/${build_byproducts}" + # The build is conducted in the directory of the Manifest, so that configuration files such as + # `.cargo/config.toml` or `toolchain.toml` are applied as expected. + WORKING_DIRECTORY "${workspace_toml_dir}" + USES_TERMINAL + COMMAND_EXPAND_LISTS + VERBATIM + ) + + # User exposed custom target, that depends on the internal target. + # Corrosion post build steps are added on the internal target, which + # ensures that they run before any user defined post build steps on this + # target. + add_custom_target( + cargo-build_${target_name} + ALL + ) + add_dependencies(cargo-build_${target_name} _cargo-build_${target_name}) + + # Add custom target before actual build that user defined custom commands (e.g. code generators) can + # use as a hook to do something before the build. This mainly exists to not expose the `_cargo-build` targets. + add_custom_target(cargo-prebuild_${target_name}) + add_dependencies(_cargo-build_${target_name} cargo-prebuild_${target_name}) + if(NOT TARGET cargo-prebuild) + add_custom_target(cargo-prebuild) + endif() + add_dependencies(cargo-prebuild cargo-prebuild_${target_name}) + + add_custom_target( + cargo-clean_${target_name} + COMMAND + "${cargo_bin}" clean ${cargo_target_option} + -p ${package_name} --manifest-path ${path_to_toml} + WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/${build_dir} + USES_TERMINAL + ) + + if (NOT TARGET cargo-clean) + add_custom_target(cargo-clean) + endif() + add_dependencies(cargo-clean cargo-clean_${target_name}) +endfunction() + +#[=======================================================================[.md: +ANCHOR: corrosion-import-crate +```cmake +corrosion_import_crate( + MANIFEST_PATH + [ALL_FEATURES] + [NO_DEFAULT_FEATURES] + [NO_STD] + [NO_LINKER_OVERRIDE] + [LOCKED] + [FROZEN] + [PROFILE ] + [IMPORTED_CRATES ] + [CRATE_TYPES ... ] + [OVERRIDE_CRATE_TYPE = ...] + [CRATES ... ] + [FEATURES ... ] + [FLAGS ... ] +) +``` +* **MANIFEST_PATH**: Path to a [Cargo.toml Manifest] file. +* **ALL_FEATURES**: Equivalent to [--all-features] passed to cargo build +* **NO_DEFAULT_FEATURES**: Equivalent to [--no-default-features] passed to cargo build +* **NO_STD**: Disable linking of standard libraries (required for no_std crates). +* **NO_LINKER_OVERRIDE**: Will let Rust/Cargo determine which linker to use instead of corrosion (when linking is invoked by Rust) +* **LOCKED**: Pass [`--locked`] to cargo build and cargo metadata. +* **FROZEN**: Pass [`--frozen`] to cargo build and cargo metadata. +* **PROFILE**: Specify cargo build profile (`dev`/`release` or a [custom profile]; `bench` and `test` are not supported) +* **IMPORTED_CRATES**: Save the list of imported crates into the variable with the provided name in the current scope. +* **CRATE_TYPES**: Only import the specified crate types. Valid values: `staticlib`, `cdylib`, `bin`. +* **OVERRIDE_CRATE_TYPE**: Override the crate-types of a cargo crate with the given comma-separated values. + Internally uses the `rustc` flag [`--crate-type`] to override the crate-type. + Valid values for the crate types are the library types `staticlib` and `cdylib`. +* **CRATES**: Only import the specified crates from a workspace. Values: Crate names. +* **FEATURES**: Enable the specified features. Equivalent to [--features] passed to `cargo build`. +* **FLAGS**: Arbitrary flags to `cargo build`. + +[custom profile]: https://doc.rust-lang.org/cargo/reference/profiles.html#custom-profiles +[--all-features]: https://doc.rust-lang.org/cargo/reference/features.html#command-line-feature-options +[--no-default-features]: https://doc.rust-lang.org/cargo/reference/features.html#command-line-feature-options +[--features]: https://doc.rust-lang.org/cargo/reference/features.html#command-line-feature-options +[`--locked`]: https://doc.rust-lang.org/cargo/commands/cargo.html#manifest-options +[`--frozen`]: https://doc.rust-lang.org/cargo/commands/cargo.html#manifest-options +[`--crate-type`]: https://doc.rust-lang.org/rustc/command-line-arguments.html#--crate-type-a-list-of-types-of-crates-for-the-compiler-to-emit +[Cargo.toml Manifest]: https://doc.rust-lang.org/cargo/appendix/glossary.html#manifest + +ANCHOR_END: corrosion-import-crate +#]=======================================================================] +function(corrosion_import_crate) + set(OPTIONS ALL_FEATURES NO_DEFAULT_FEATURES NO_STD NO_LINKER_OVERRIDE LOCKED FROZEN) + set(ONE_VALUE_KEYWORDS MANIFEST_PATH PROFILE IMPORTED_CRATES) + set(MULTI_VALUE_KEYWORDS CRATE_TYPES CRATES FEATURES FLAGS OVERRIDE_CRATE_TYPE) + cmake_parse_arguments(COR "${OPTIONS}" "${ONE_VALUE_KEYWORDS}" "${MULTI_VALUE_KEYWORDS}" ${ARGN}) + list(APPEND CMAKE_MESSAGE_CONTEXT "corrosion_import_crate") + + if(DEFINED COR_UNPARSED_ARGUMENTS) + message(AUTHOR_WARNING "Unexpected arguments: " ${COR_UNPARSED_ARGUMENTS} + "\nCorrosion will ignore these unexpected arguments." + ) + endif() + if(DEFINED COR_KEYWORDS_MISSING_VALUES) + message(DEBUG "Note: the following keywords passed to corrosion_import_crate had no associated value(s): " + ${COR_KEYWORDS_MISSING_VALUES} + ) + endif() + if (NOT DEFINED COR_MANIFEST_PATH) + message(FATAL_ERROR "MANIFEST_PATH is a required keyword to corrosion_add_crate") + endif() + _corrosion_option_passthrough_helper(NO_LINKER_OVERRIDE COR no_linker_override) + _corrosion_option_passthrough_helper(LOCKED COR locked) + _corrosion_option_passthrough_helper(FROZEN COR frozen) + _corrosion_arg_passthrough_helper(CRATES COR crate_allowlist) + _corrosion_arg_passthrough_helper(CRATE_TYPES COR crate_types) + + if(COR_PROFILE) + if(Rust_VERSION VERSION_LESS 1.57.0) + message(FATAL_ERROR "Selecting custom profiles via `PROFILE` requires at least rust 1.57.0, but you " + "have ${Rust_VERSION}." + ) + # The profile name could be part of a Generator expression, so this won't catch all occurences. + # Since it is hard to add an error message for genex, we don't do that here. + elseif("${COR_PROFILE}" STREQUAL "test" OR "${COR_PROFILE}" STREQUAL "bench") + message(FATAL_ERROR "Corrosion does not support building Rust crates with the cargo profiles" + " `test` or `bench`. These profiles add a hash to the output artifact name that we" + " cannot predict. Please consider using a custom cargo profile which inherits from the" + " built-in profile instead." + ) + endif() + endif() + + # intended to be used with foreach(... ZIP_LISTS ...), meaning + # that the crate_types at index i of `override_crate_type_types_list` are + # for the package_name at index i of `override_crate_type_package_name_list`. + # It would really be nice if CMake had structs or dicts. + unset(override_crate_type_package_name_list) + unset(override_crate_type_types_list) + unset(OVERRIDE_CRATE_TYPE_ARGS) + if(DEFINED COR_OVERRIDE_CRATE_TYPE) + string(JOIN " " usage_help + "Each argument to OVERRIDE_CRATE_TYPE must be of the form `=." + "The package_name must be a valid cargo package name and the crate_type must be " + "a comma-seperated list with valid values being `staticlib`, `cdylib` and `bin`" + ) + foreach(entry IN LISTS COR_OVERRIDE_CRATE_TYPE) + string(REPLACE "=" ";" key_val_list ${entry}) + list(LENGTH key_val_list key_val_list_len) + if(NOT key_val_list_len EQUAL "2") + message(FATAL_ERROR "Invalid argument: `${entry}` for parameter OVERRIDE_CRATE_TYPE!\n" + "${usage_help}" + ) + endif() + list(GET key_val_list "0" package_name) + list(GET key_val_list "1" crate_types) + list(APPEND override_crate_type_package_name_list "${package_name}") + list(APPEND override_crate_type_types_list "${crate_types}") + endforeach() + list(LENGTH override_crate_type_package_name_list num_override_packages) + list(LENGTH override_crate_type_types_list num_override_packages2) + if("${Rust_VERSION}" VERSION_LESS "1.64") + message(WARNING "OVERRIDE_CRATE_TYPE requires at Rust 1.64 or newer. Ignoring the option") + elseif(NOT num_override_packages EQUAL num_override_packages2) + message(WARNING "Internal error while parsing OVERRIDE_CRATE_TYPE arguments.\n" + "Corrosion will ignore this argument and continue." + ) + else() + # Pass by ref: we intentionally pass the list names here! + set(override_crate_types_arg "OVERRIDE_CRATE_TYPE_ARGS" "override_crate_type_package_name_list" "override_crate_type_types_list") + endif() + endif() + + if (NOT IS_ABSOLUTE "${COR_MANIFEST_PATH}") + set(COR_MANIFEST_PATH ${CMAKE_CURRENT_SOURCE_DIR}/${COR_MANIFEST_PATH}) + endif() + + set(additional_cargo_flags ${COR_FLAGS}) + + if(COR_LOCKED AND NOT "--locked" IN_LIST additional_cargo_flags) + list(APPEND additional_cargo_flags "--locked") + endif() + if(COR_FROZEN AND NOT "--frozen" IN_LIST additional_cargo_flags) + list(APPEND additional_cargo_flags "--frozen") + endif() + + set(imported_crates "") + + _generator_add_cargo_targets( + MANIFEST_PATH + "${COR_MANIFEST_PATH}" + IMPORTED_CRATES + imported_crates + ${crate_allowlist} + ${crate_types} + ${no_linker_override} + ${override_crate_types_arg} + ) + + # Not target props yet: + # NO_STD + # NO_LINKER_OVERRIDE # We could simply zero INTERFACE_CORROSION_LINKER if this is set. + # LOCKED / FROZEN get merged into FLAGS after cargo metadata. + + # Initialize the target properties with the arguments to corrosion_import_crate. + set_target_properties( + ${imported_crates} + PROPERTIES + "${_CORR_PROP_ALL_FEATURES}" "${COR_ALL_FEATURES}" + "${_CORR_PROP_NO_DEFAULT_FEATURES}" "${COR_NO_DEFAULT_FEATURES}" + "${_CORR_PROP_FEATURES}" "${COR_FEATURES}" + INTERFACE_CORROSION_CARGO_PROFILE "${COR_PROFILE}" + INTERFACE_CORROSION_CARGO_FLAGS "${additional_cargo_flags}" + ) + + # _CORR_PROP_ENV_VARS + if(DEFINED COR_IMPORTED_CRATES) + set(${COR_IMPORTED_CRATES} ${imported_crates} PARENT_SCOPE) + endif() +endfunction() + +function(corrosion_set_linker target_name linker) + if(NOT linker) + message(FATAL_ERROR "The linker passed to `corrosion_set_linker` may not be empty") + elseif(NOT TARGET "${target_name}") + message(FATAL_ERROR "The target `${target_name}` does not exist.") + endif() + if(MSVC) + message(WARNING "Explicitly setting the linker with the MSVC toolchain is currently not supported and ignored") + endif() + + if(TARGET "${target_name}-static" AND NOT TARGET "${target_name}-shared") + message(WARNING "The target ${target_name} builds a static library." + "The linker is never invoked for a static library so specifying a linker has no effect." + ) + endif() + + set_property( + TARGET ${target_name} + PROPERTY INTERFACE_CORROSION_LINKER "${linker}" + ) +endfunction() + +function(corrosion_set_hostbuild target_name) + # Configure the target to be compiled for the Host target and ignore any cross-compile configuration. + set_property( + TARGET ${target_name} + PROPERTY ${_CORR_PROP_HOST_BUILD} 1 + ) +endfunction() + +# Add flags for rustc (RUSTFLAGS) which affect the target and all of it's Rust dependencies +# +# Additional rustflags may be passed as optional parameters after rustflag. +# Please note, that if you import multiple targets from a package or workspace, but set different +# Rustflags via this function, the Rust dependencies will have to be rebuilt when changing targets. +# Consider `corrosion_add_target_local_rustflags()` as an alternative to avoid this. +function(corrosion_add_target_rustflags target_name rustflag) + # Additional rustflags may be passed as optional parameters after rustflag. + set_property( + TARGET ${target_name} + APPEND + PROPERTY INTERFACE_CORROSION_RUSTFLAGS ${rustflag} ${ARGN} + ) +endfunction() + +# Add flags for rustc (RUSTFLAGS) which only affect the target, but none of it's (Rust) dependencies +# +# Additional rustflags may be passed as optional parameters after rustc_flag. +function(corrosion_add_target_local_rustflags target_name rustc_flag) + # Set Rustflags via `cargo rustc` which only affect the current crate, but not dependencies. + set_property( + TARGET ${target_name} + APPEND + PROPERTY INTERFACE_CORROSION_LOCAL_RUSTFLAGS ${rustc_flag} ${ARGN} + ) +endfunction() + +function(corrosion_set_env_vars target_name env_var) + # Additional environment variables may be passed as optional parameters after env_var. + set_property( + TARGET ${target_name} + APPEND + PROPERTY ${_CORR_PROP_ENV_VARS} ${env_var} ${ARGN} + ) +endfunction() + +function(corrosion_set_cargo_flags target_name) + # corrosion_set_cargo_flags( [ ... ]) + + set_property( + TARGET ${target_name} + APPEND + PROPERTY INTERFACE_CORROSION_CARGO_FLAGS ${ARGN} + ) +endfunction() + +function(corrosion_set_features target_name) + # corrosion_set_features( [ALL_FEATURES=Bool] [NO_DEFAULT_FEATURES] [FEATURES ... ]) + set(options NO_DEFAULT_FEATURES) + set(one_value_args ALL_FEATURES) + set(multi_value_args FEATURES) + cmake_parse_arguments( + PARSE_ARGV 1 + SET + "${options}" + "${one_value_args}" + "${multi_value_args}" + ) + + if(DEFINED SET_ALL_FEATURES) + set_property( + TARGET ${target_name} + PROPERTY ${_CORR_PROP_ALL_FEATURES} ${SET_ALL_FEATURES} + ) + endif() + if(SET_NO_DEFAULT_FEATURES) + set_property( + TARGET ${target_name} + PROPERTY ${_CORR_PROP_NO_DEFAULT_FEATURES} 1 + ) + endif() + if(SET_FEATURES) + set_property( + TARGET ${target_name} + APPEND + PROPERTY ${_CORR_PROP_FEATURES} ${SET_FEATURES} + ) + endif() +endfunction() + +function(corrosion_link_libraries target_name) + if(TARGET "${target_name}-static") + message(DEBUG "The target ${target_name} builds a static Rust library." + "Calling `target_link_libraries()` instead." + ) + target_link_libraries("${target_name}-static" INTERFACE ${ARGN}) + if(NOT TARGET "${target_name}-shared") + # Early return, since Rust won't invoke the linker for static libraries + return() + endif() + endif() + foreach(library ${ARGN}) + set_property( + TARGET _cargo-build_${target_name} + APPEND + PROPERTY CARGO_DEPS_LINKER_LANGUAGES + $ + ) + + if (TARGET "${library}") + corrosion_add_target_local_rustflags(${target_name} + "-L$" + "-l$" + ) + add_dependencies(_cargo-build_${target_name} ${library}) + elseif(IS_ABSOLUTE "${library}") + # Linking via full path (See https://doc.rust-lang.org/rustc/command-line-arguments.html#linking-modifiers-verbatim) + corrosion_add_target_local_rustflags(${target_name} "-Clink-arg=${library}") + else() + # We have to assume ${library} is a non-CMake library name + corrosion_add_target_local_rustflags(${target_name} "-l${library}") + endif() + endforeach() +endfunction() + +#[=======================================================================[.md: +ANCHOR: corrosion-install +** EXPERIMENTAL **: This function is currently still considered experimental + and is not officially released yet. Feedback and Suggestions are welcome. + +```cmake +corrosion_install(TARGETS ... [EXPORT ] + [[ARCHIVE|LIBRARY|RUNTIME|PUBLIC_HEADER] + [DESTINATION ] + [PERMISSIONS ] + [CONFIGURATIONS [Debug|Release|]] + ] [...]) +``` +* **TARGETS**: Target or targets to install. +* **EXPORT**: Creates an export that can be installed with `install(EXPORT)`. must be globally unique. + Also creates a file at ${CMAKE_BINARY_DIR}/corrosion/Corrosion.cmake that must be included in the installed config file. +* **ARCHIVE**/**LIBRARY**/**RUNTIME**/PUBLIC_HEADER: Designates that the following settings only apply to that specific type of object. +* **DESTINATION**: The subdirectory within the CMAKE_INSTALL_PREFIX that a specific object should be placed. Defaults to values from GNUInstallDirs. +* **PERMISSIONS**: The permissions of files copied into the install prefix. + +Any `PUBLIC` or `INTERFACE` [file sets] will be installed. + +[file sets]: https://cmake.org/cmake/help/latest/command/target_sources.html#file-sets + +ANCHOR_END: corrosion-install +#]=======================================================================] +function(corrosion_install) + # Default install dirs + include(GNUInstallDirs) + + # Parse arguments to corrosion_install + list(GET ARGN 0 INSTALL_TYPE) + list(REMOVE_AT ARGN 0) + + # The different install types that are supported. Some targets may have more than one of these + # types. For example, on Windows, a shared library will have both an ARCHIVE component and a + # RUNTIME component. + set(INSTALL_TARGET_TYPES ARCHIVE LIBRARY RUNTIME PRIVATE_HEADER PUBLIC_HEADER) + + # Arguments to each install target type + set(OPTIONS) + set(ONE_VALUE_ARGS DESTINATION) + set(MULTI_VALUE_ARGS PERMISSIONS CONFIGURATIONS) + set(TARGET_ARGS ${OPTIONS} ${ONE_VALUE_ARGS} ${MULTI_VALUE_ARGS}) + + if (INSTALL_TYPE STREQUAL "TARGETS") + # Extract targets + set(INSTALL_TARGETS) + list(LENGTH ARGN ARGN_LENGTH) + set(DELIMITERS EXPORT ${INSTALL_TARGET_TYPES} ${TARGET_ARGS}) + while(ARGN_LENGTH) + # If we hit another keyword, stop - we've found all the targets + list(GET ARGN 0 FRONT) + if (FRONT IN_LIST DELIMITERS) + break() + endif() + + list(APPEND INSTALL_TARGETS ${FRONT}) + list(REMOVE_AT ARGN 0) + + # Update ARGN_LENGTH + list(LENGTH ARGN ARGN_LENGTH) + endwhile() + + # Check if there are any args left before proceeding + list(LENGTH ARGN ARGN_LENGTH) + if (ARGN_LENGTH) + list(GET ARGN 0 FRONT) + if (FRONT STREQUAL "EXPORT") + list(REMOVE_AT ARGN 0) # Pop "EXPORT" + + list(GET ARGN 0 EXPORT_NAME) + list(REMOVE_AT ARGN 0) # Pop + set(EXTRA_TARGETS_EXPORT_NAME ${EXPORT_NAME}Corrosion.cmake) + set(EXPORT_NAME EXPORT ${EXPORT_NAME}) + set(EXPORT_FILE_PATH "${CMAKE_BINARY_DIR}/corrosion/${EXTRA_TARGETS_EXPORT_NAME}") + # Remove first, since otherwise we will append to the file on every reconfigure. + # Assumes that the corrosion_install will only be called once for a given EXPORT_NAME. + file(REMOVE "${EXPORT_FILE_PATH}") + endif() + else() + # Prevent variable set in user code from interfering + set(EXPORT_NAME) + endif() + + # Loop over all arguments and get options for each install target type + list(LENGTH ARGN ARGN_LENGTH) + while(ARGN_LENGTH) + # Check if we're dealing with arguments for a specific install target type, or with + # default options for all target types. + list(GET ARGN 0 FRONT) + if (FRONT IN_LIST INSTALL_TARGET_TYPES) + set(INSTALL_TARGET_TYPE ${FRONT}) + list(REMOVE_AT ARGN 0) + else() + set(INSTALL_TARGET_TYPE DEFAULT) + endif() + + # Gather the arguments to this install type + set(ARGS) + while(ARGN_LENGTH) + # If the next keyword is an install target type, then break - arguments have been + # gathered. + list(GET ARGN 0 FRONT) + if (FRONT IN_LIST INSTALL_TARGET_TYPES) + break() + endif() + + list(APPEND ARGS ${FRONT}) + list(REMOVE_AT ARGN 0) + + list(LENGTH ARGN ARGN_LENGTH) + endwhile() + + # Parse the arguments and register the file install + cmake_parse_arguments( + COR "${OPTIONS}" "${ONE_VALUE_ARGS}" "${MULTI_VALUE_ARGS}" ${ARGS}) + + if (COR_DESTINATION) + set(COR_INSTALL_${INSTALL_TARGET_TYPE}_DESTINATION ${COR_DESTINATION}) + endif() + + if (COR_PERMISSIONS) + set(COR_INSTALL_${INSTALL_TARGET_TYPE}_PERMISSIONS ${COR_PERMISSIONS}) + endif() + + if (COR_CONFIGURATIONS) + set(COR_INSTALL_${INSTALL_TARGET_TYPE}_CONFIGURATIONS ${COR_CONFIGURATIONS}) + endif() + + # Update ARG_LENGTH + list(LENGTH ARGN ARGN_LENGTH) + endwhile() + + # Default permissions for all files + set(DEFAULT_PERMISSIONS OWNER_WRITE OWNER_READ GROUP_READ WORLD_READ) + + # Loop through each install target and register file installations + foreach(INSTALL_TARGET ${INSTALL_TARGETS}) + if(NOT TARGET ${INSTALL_TARGET}) + message(FATAL_ERROR "Install target ${INSTALL_TARGET} is not a valid target") + endif() + # Don't both implementing target type differentiation using generator expressions since + # TYPE cannot change after target creation + get_property( + TARGET_TYPE + TARGET ${INSTALL_TARGET} PROPERTY TYPE + ) + + # Install executable files first + if (TARGET_TYPE STREQUAL "EXECUTABLE") + if (DEFINED COR_INSTALL_RUNTIME_DESTINATION) + set(DESTINATION ${COR_INSTALL_RUNTIME_DESTINATION}) + elseif (DEFINED COR_INSTALL_DEFAULT_DESTINATION) + set(DESTINATION ${COR_INSTALL_DEFAULT_DESTINATION}) + else() + set(DESTINATION ${CMAKE_INSTALL_BINDIR}) + endif() + + if (DEFINED COR_INSTALL_RUNTIME_PERMISSIONS) + set(PERMISSIONS ${COR_INSTALL_RUNTIME_PERMISSIONS}) + elseif (DEFINED COR_INSTALL_DEFAULT_PERMISSIONS) + set(PERMISSIONS ${COR_INSTALL_DEFAULT_PERMISSIONS}) + else() + set( + PERMISSIONS + ${DEFAULT_PERMISSIONS} OWNER_EXECUTE GROUP_EXECUTE WORLD_EXECUTE) + endif() + + if (DEFINED COR_INSTALL_RUNTIME_CONFIGURATIONS) + set(CONFIGURATIONS CONFIGURATIONS ${COR_INSTALL_RUNTIME_CONFIGURATIONS}) + elseif (DEFINED COR_INSTALL_DEFAULT_CONFIGURATIONS) + set(CONFIGURATIONS CONFIGURATIONS ${COR_INSTALL_DEFAULT_CONFIGURATIONS}) + else() + set(CONFIGURATIONS) + endif() + + install( + FILES $ + DESTINATION ${DESTINATION} + PERMISSIONS ${PERMISSIONS} + ${CONFIGURATIONS} + ) + elseif(TARGET_TYPE STREQUAL "INTERFACE_LIBRARY") + if(TARGET ${INSTALL_TARGET}-static) + if (DEFINED COR_INSTALL_ARCHIVE_DESTINATION) + set(DESTINATION ${COR_INSTALL_ARCHIVE_DESTINATION}) + elseif (DEFINED COR_INSTALL_DEFAULT_DESTINATION) + set(DESTINATION ${COR_INSTALL_DEFAULT_DESTINATION}) + else() + set(DESTINATION ${CMAKE_INSTALL_LIBDIR}) + endif() + + if (DEFINED COR_INSTALL_ARCHIVE_PERMISSIONS) + set(PERMISSIONS ${COR_INSTALL_ARCHIVE_PERMISSIONS}) + elseif (DEFINED COR_INSTALL_DEFAULT_PERMISSIONS) + set(PERMISSIONS ${COR_INSTALL_DEFAULT_PERMISSIONS}) + else() + set(PERMISSIONS ${DEFAULT_PERMISSIONS}) + endif() + + if (DEFINED COR_INSTALL_ARCHIVE_CONFIGURATIONS) + set(CONFIGURATIONS CONFIGURATIONS ${COR_INSTALL_ARCHIVE_CONFIGURATIONS}) + elseif (DEFINED COR_INSTALL_DEFAULT_CONFIGURATIONS) + set(CONFIGURATIONS CONFIGURATIONS ${COR_INSTALL_DEFAULT_CONFIGURATIONS}) + else() + set(CONFIGURATIONS) + endif() + + install( + FILES $ + DESTINATION ${DESTINATION} + PERMISSIONS ${PERMISSIONS} + ${CONFIGURATIONS} + ) + + if(EXPORT_NAME) + get_target_property(COR_FILE_NAME ${INSTALL_TARGET}-static COR_FILE_NAME) + file(APPEND "${EXPORT_FILE_PATH}" +" +add_library(${INSTALL_TARGET}-static STATIC IMPORTED) +set_target_properties(${INSTALL_TARGET}-static + PROPERTIES + IMPORTED_LOCATION \"\${PACKAGE_PREFIX_DIR}/${DESTINATION}/${COR_FILE_NAME}\" +) +" + ) + endif() + endif() + + if(TARGET ${INSTALL_TARGET}-shared) + if (DEFINED COR_INSTALL_LIBRARY_DESTINATION) + set(DESTINATION ${COR_INSTALL_LIBRARY_DESTINATION}) + elseif (DEFINED COR_INSTALL_DEFAULT_DESTINATION) + set(DESTINATION ${COR_INSTALL_DEFAULT_DESTINATION}) + else() + set(DESTINATION ${CMAKE_INSTALL_LIBDIR}) + endif() + + if (DEFINED COR_INSTALL_LIBRARY_PERMISSIONS) + set(PERMISSIONS ${COR_INSTALL_LIBRARY_PERMISSIONS}) + elseif (DEFINED COR_INSTALL_DEFAULT_PERMISSIONS) + set(PERMISSIONS ${COR_INSTALL_DEFAULT_PERMISSIONS}) + else() + set( + PERMISSIONS + ${DEFAULT_PERMISSIONS} OWNER_EXECUTE GROUP_EXECUTE WORLD_EXECUTE + ) + endif() + + if (DEFINED COR_INSTALL_LIBRARY_CONFIGURATIONS) + set(CONFIGURATIONS CONFIGURATIONS ${COR_INSTALL_LIBRARY_CONFIGURATIONS}) + elseif (DEFINED COR_INSTALL_DEFAULT_CONFIGURATIONS) + set(CONFIGURATIONS CONFIGURATIONS ${COR_INSTALL_DEFAULT_CONFIGURATIONS}) + else() + set(CONFIGURATIONS) + endif() + + install( + IMPORTED_RUNTIME_ARTIFACTS ${INSTALL_TARGET}-shared + PERMISSIONS ${PERMISSIONS} + DESTINATION ${DESTINATION} + ${CONFIGURATIONS} + ) + + if(EXPORT_NAME) + get_target_property(COR_FILE_NAME ${INSTALL_TARGET}-shared COR_FILE_NAME) + file(APPEND "${EXPORT_FILE_PATH}" +" +add_library(${INSTALL_TARGET}-shared SHARED IMPORTED) +set_target_properties(${INSTALL_TARGET}-shared + PROPERTIES + IMPORTED_LOCATION \"\${PACKAGE_PREFIX_DIR}/${DESTINATION}/${COR_FILE_NAME}\" +) +" + ) + + get_target_property(COR_IMPLIB_FILE_NAME ${INSTALL_TARGET}-shared COR_IMPLIB_FILE_NAME) + if (NOT COR_IMPLIB_FILE_NAME MATCHES .*-NOTFOUND) + file(APPEND "${EXPORT_FILE_PATH}" +" +set_target_properties(${INSTALL_TARGET}-shared + PROPERTIES + IMPORTED_IMPLIB \"\${PACKAGE_PREFIX_DIR}/${DESTINATION}/${COR_IMPLIB_FILE_NAME}\" +)" + ) + endif() + endif() + endif() + else() + message(FATAL_ERROR "Unknown target type ${TARGET_TYPE} for install target ${INSTALL_TARGET}") + endif() + + # Executables can also have export tables, so they _might_ also need header files + if (DEFINED COR_INSTALL_PUBLIC_HEADER_DESTINATION) + set(DESTINATION ${COR_INSTALL_PUBLIC_HEADER_DESTINATION}) + elseif (DEFINED COR_INSTALL_DEFAULT_DESTINATION) + set(DESTINATION ${COR_INSTALL_DEFAULT_DESTINATION}) + else() + set(DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) + endif() + + if (DEFINED COR_INSTALL_PUBLIC_HEADER_PERMISSIONS) + set(PERMISSIONS ${COR_INSTALL_PUBLIC_HEADER_PERMISSIONS}) + elseif (DEFINED COR_INSTALL_DEFAULT_PERMISSIONS) + set(PERMISSIONS ${COR_INSTALL_DEFAULT_PERMISSIONS}) + else() + # Directories need OWNER_EXECUTE in order to be deletable by owner + set(PERMISSIONS ${DEFAULT_PERMISSIONS} OWNER_EXECUTE) + endif() + + if (DEFINED COR_INSTALL_PUBLIC_HEADER_CONFIGURATIONS) + set(CONFIGURATIONS CONFIGURATIONS ${COR_INSTALL_PUBLIC_HEADER_CONFIGURATIONS}) + elseif (DEFINED COR_INSTALL_DEFAULT_CONFIGURATIONS) + set(CONFIGURATIONS CONFIGURATIONS ${COR_INSTALL_DEFAULT_CONFIGURATIONS}) + else() + set(CONFIGURATIONS) + endif() + + get_target_property(FILE_SET ${INSTALL_TARGET} INTERFACE_HEADER_SETS) + if(NOT FILE_SET OR FILE_SET MATCHES .*-NOTFOUND) + set(TARGET_HAS_FILE_SET FALSE) + else() + set(TARGET_HAS_FILE_SET TRUE) + endif() + + if(NOT TARGET_HAS_FILE_SET) + if(EXPORT_NAME) + # We still need to generate a EXPORT but we can't do that with install(DIRECTORY) + install(TARGETS ${INSTALL_TARGET} ${EXPORT_NAME}) + endif() + + set(PUBLIC_HEADER_PROPERTIES INCLUDE_DIRECTORIES PUBLIC_INCLUDE_DIRECTORIES INTERFACE_INCLUDE_DIRECTORIES) + foreach(PUBLIC_HEADER_PROPERTY ${PUBLIC_HEADER_PROPERTIES}) + get_target_property(PUBLIC_HEADER ${INSTALL_TARGET} ${PUBLIC_HEADER_PROPERTY}) + + if(NOT PUBLIC_HEADER MATCHES .*-NOTFOUND) + foreach(INCLUDE_DIRECTORY ${PUBLIC_HEADER}) + install( + DIRECTORY ${INCLUDE_DIRECTORY} + DESTINATION . + FILE_PERMISSIONS ${PERMISSIONS} + DIRECTORY_PERMISSIONS ${PERMISSIONS} + ${CONFIGURATIONS} + ) + endforeach() + endif() + endforeach() + else() + install( + TARGETS ${INSTALL_TARGET} + ${EXPORT_NAME} + FILE_SET HEADERS + DESTINATION ${DESTINATION} + PERMISSIONS ${PERMISSIONS} + ${CONFIGURATIONS} + ) + endif() + endforeach() + + elseif(INSTALL_TYPE STREQUAL "EXPORT") + message(FATAL_ERROR "install(EXPORT ...) not yet implemented") + else() + message(FATAL_ERROR "Unknown arg: ${INSTALL_TYPE}") + endif() +endfunction() + +#[=======================================================================[.md: +** EXPERIMENTAL **: This function is currently still considered experimental + and is not officially released yet. Feedback and Suggestions are welcome. + +ANCHOR: corrosion_add_cxxbridge + +```cmake +corrosion_add_cxxbridge(cxx_target + CRATE + REGEN_TARGET + [FILES ] +) +``` + +Adds build-rules to create C++ bindings using the [cxx] crate. + +### Arguments: +* `cxxtarget`: Name of the C++ library target for the bindings, which corrosion will create. +* **FILES**: Input Rust source file containing #[cxx::bridge]. +* **CRATE**: Name of an imported Rust target. Note: Parameter may be renamed before release +* **REGEN_TARGET**: Name of a custom target that will regenerate the cxx bindings **without** recompiling. Note: Parameter may be renamed before release + +#### Currently missing arguments + +The following arguments to cxxbridge **currently** have no way to be passed by the user: +- `--cfg` +- `--cxx-impl-annotations` +- `--include` + +The created rules approximately do the following: +- Check which version of `cxx` the Rust crate specified by the `CRATE` argument depends on. +- Check if the exact same version of `cxxbridge-cmd` is installed (available in `PATH`) +- If not, create a rule to build the exact same version of `cxxbridge-cmd`. +- Create rules to run `cxxbridge` and generate + - The `rust/cxx.h` header + - A header and source file for each of the files specified in `FILES` +- The generated sources (and header include directories) are added to the `cxxtarget` CMake + library target. + +### Limitations + +We currently require the `CRATE` argument to be a target imported by Corrosion, however, +Corrosion does not import `rlib` only libraries. As a workaround users can add +`staticlib` to their list of crate kinds. In the future this may be solved more properly, +by either adding an option to also import Rlib targets (without build rules) or by +adding a `MANIFEST_PATH` argument to this function, specifying where the crate is. + +### Contributing + +Specifically some more realistic test / demo projects and feedback about limitations would be +welcome. + +[cxx]: https://github.com/dtolnay/cxx + +ANCHOR_END: corrosion_add_cxxbridge +#]=======================================================================] +function(corrosion_add_cxxbridge cxx_target) + set(OPTIONS) + set(ONE_VALUE_KEYWORDS CRATE REGEN_TARGET) + set(MULTI_VALUE_KEYWORDS FILES) + cmake_parse_arguments(PARSE_ARGV 1 _arg "${OPTIONS}" "${ONE_VALUE_KEYWORDS}" "${MULTI_VALUE_KEYWORDS}") + + set(required_keywords CRATE FILES) + foreach(keyword ${required_keywords}) + if(NOT DEFINED "_arg_${keyword}") + message(FATAL_ERROR "Missing required parameter `${keyword}`.") + elseif("${_arg_${keyword}}" STREQUAL "") + message(FATAL_ERROR "Required parameter `${keyword}` may not be set to an empty string.") + endif() + endforeach() + + get_target_property(manifest_path "${_arg_CRATE}" INTERFACE_COR_PACKAGE_MANIFEST_PATH) + + if(NOT EXISTS "${manifest_path}") + message(FATAL_ERROR "Internal error: No package manifest found at ${manifest_path}") + endif() + + get_filename_component(manifest_dir ${manifest_path} DIRECTORY) + + execute_process(COMMAND ${CMAKE_COMMAND} -E env + "CARGO_BUILD_RUSTC=${_CORROSION_RUSTC}" + ${_CORROSION_CARGO} tree -i cxx --depth=0 + WORKING_DIRECTORY "${manifest_dir}" + RESULT_VARIABLE cxx_version_result + OUTPUT_VARIABLE cxx_version_output + ) + if(NOT "${cxx_version_result}" EQUAL "0") + message(FATAL_ERROR "Crate ${_arg_CRATE} does not depend on cxx.") + endif() + if(cxx_version_output MATCHES "cxx v([0-9]+.[0-9]+.[0-9]+)") + set(cxx_required_version "${CMAKE_MATCH_1}") + else() + message(FATAL_ERROR "Failed to parse cxx version from cargo tree output: `cxx_version_output`") + endif() + + # First check if a suitable version of cxxbridge is installed + find_program(INSTALLED_CXXBRIDGE cxxbridge PATHS "$ENV{HOME}/.cargo/bin/") + mark_as_advanced(INSTALLED_CXXBRIDGE) + if(INSTALLED_CXXBRIDGE) + execute_process(COMMAND ${INSTALLED_CXXBRIDGE} --version OUTPUT_VARIABLE cxxbridge_version_output) + if(cxxbridge_version_output MATCHES "cxxbridge ([0-9]+.[0-9]+.[0-9]+)") + set(cxxbridge_version "${CMAKE_MATCH_1}") + else() + set(cxxbridge_version "") + endif() + endif() + + set(cxxbridge "") + if(cxxbridge_version) + if(cxxbridge_version VERSION_EQUAL cxx_required_version) + set(cxxbridge "${INSTALLED_CXXBRIDGE}") + if(NOT TARGET "cxxbridge_v${cxx_required_version}") + # Add an empty target. + add_custom_target("cxxbridge_v${cxx_required_version}" + ) + endif() + endif() + endif() + + # No suitable version of cxxbridge was installed, so use custom target to build correct version. + if(NOT cxxbridge) + if(NOT TARGET "cxxbridge_v${cxx_required_version}") + unset(executable_postfix) + if(Rust_CARGO_HOST_OS STREQUAL "windows") + set(executable_postfix ".exe") + endif() + add_custom_command(OUTPUT "${CMAKE_BINARY_DIR}/corrosion/cxxbridge_v${cxx_required_version}/bin/cxxbridge${executable_postfix}" + COMMAND + ${CMAKE_COMMAND} -E make_directory "${CMAKE_BINARY_DIR}/corrosion/cxxbridge_v${cxx_required_version}" + COMMAND + ${CMAKE_COMMAND} -E env + "CARGO_BUILD_RUSTC=${_CORROSION_RUSTC}" + ${_CORROSION_CARGO} install + cxxbridge-cmd + --version "${cxx_required_version}" + --root "${CMAKE_BINARY_DIR}/corrosion/cxxbridge_v${cxx_required_version}" + --quiet + # todo: use --target-dir to potentially reuse artifacts + COMMENT "Building cxxbridge (version ${cxx_required_version})" + ) + add_custom_target("cxxbridge_v${cxx_required_version}" + DEPENDS "${CMAKE_BINARY_DIR}/corrosion/cxxbridge_v${cxx_required_version}/bin/cxxbridge${executable_postfix}" + ) + endif() + set(cxxbridge "${CMAKE_BINARY_DIR}/corrosion/cxxbridge_v${cxx_required_version}/bin/cxxbridge${executable_postfix}") + endif() + + + # The generated folder structure will be of the following form + # + # CMAKE_CURRENT_BINARY_DIR + # corrosion_generated + # cxxbridge + # + # include + # + # + # rust + # cxx.h + # src + # + # cbindgen + # ... + # other + # ... + + set(corrosion_generated_dir "${CMAKE_CURRENT_BINARY_DIR}/corrosion_generated") + set(generated_dir "${corrosion_generated_dir}/cxxbridge/${cxx_target}") + set(header_placement_dir "${generated_dir}/include/${cxx_target}") + set(source_placement_dir "${generated_dir}/src") + + add_library(${cxx_target} STATIC) + target_include_directories(${cxx_target} + PUBLIC + $ + $ + ) + + # cxx generated code is using c++11 features in headers, so propagate c++11 as minimal requirement + target_compile_features(${cxx_target} PUBLIC cxx_std_11) + + # Todo: target_link_libraries is only necessary for rust2c projects. + # It is possible that checking if the rust crate is an executable is a sufficient check, + # but some more thought may be needed here. + # Maybe we should also let the user do this, since for c2rust, the user also has to call + # corrosion_link_libraries() themselves. + get_target_property(crate_target_type ${_arg_CRATE} TYPE) + if (NOT crate_target_type STREQUAL "EXECUTABLE") + target_link_libraries(${cxx_target} PRIVATE ${_arg_CRATE}) + endif() + + file(MAKE_DIRECTORY "${generated_dir}/include/rust") + add_custom_command( + OUTPUT "${generated_dir}/include/rust/cxx.h" + COMMAND + ${cxxbridge} --header --output "${generated_dir}/include/rust/cxx.h" + DEPENDS "cxxbridge_v${cxx_required_version}" + COMMENT "Generating rust/cxx.h header" + ) + + set(GENERATED_FILES "${generated_dir}/include/rust/cxx.h") + + foreach(filepath ${_arg_FILES}) + get_filename_component(filename ${filepath} NAME_WE) + get_filename_component(directory ${filepath} DIRECTORY) + set(directory_component "") + if(directory) + set(directory_component "${directory}/") + endif() + # todo: convert potentially absolute paths to relative paths.. + set(cxx_header ${directory_component}${filename}.h) + set(cxx_source ${directory_component}${filename}.cpp) + + # todo: not all projects may use the `src` directory. + set(rust_source_path "${manifest_dir}/src/${filepath}") + + file(MAKE_DIRECTORY "${header_placement_dir}/${directory}" "${source_placement_dir}/${directory}") + + add_custom_command( + OUTPUT + "${header_placement_dir}/${cxx_header}" + "${source_placement_dir}/${cxx_source}" + COMMAND + ${cxxbridge} ${rust_source_path} --header --output "${header_placement_dir}/${cxx_header}" + COMMAND + ${cxxbridge} ${rust_source_path} + --output "${source_placement_dir}/${cxx_source}" + --include "${cxx_target}/${cxx_header}" + DEPENDS "cxxbridge_v${cxx_required_version}" "${rust_source_path}" + COMMENT "Generating cxx bindings for crate ${_arg_CRATE} and file src/${filepath}" + ) + + list(APPEND GENERATED_FILES + "${header_placement_dir}/${cxx_header}" + "${source_placement_dir}/${cxx_source}") + endforeach() + target_sources(${cxx_target} PRIVATE ${GENERATED_FILES}) + + if(DEFINED _arg_REGEN_TARGET) + add_custom_target(${_arg_REGEN_TARGET} + DEPENDS ${GENERATED_FILES} + COMMENT "Generated cxx bindings for crate ${_arg_CRATE}") + endif() + +endfunction() + +#[=======================================================================[.md: +ANCHOR: corrosion_cbindgen +```cmake +corrosion_cbindgen( + TARGET + HEADER_NAME + [CARGO_PACKAGE ] + [MANIFEST_DIRECTORY ] + [CBINDGEN_VERSION ] + [FLAGS ... ] +) +``` + +A helper function which uses [cbindgen] to generate C/C++ bindings for a Rust crate. +If `cbindgen` is not in `PATH` the helper function will automatically try to download +`cbindgen` and place the built binary into `CMAKE_BINARY_DIR`. The binary is shared +between multiple invocations of this function. + + +* **TARGET**: The name of an imported Rust library target, for which bindings should be generated. + If the target was not previously imported by Corrosion, because the crate only produces an + `rlib`, you must additionally specify `MANIFEST_DIRECTORY`. + +* **MANIFEST_DIRECTORY**: Directory of the package defining the library crate bindings should be generated for. + If you want to avoid specifying `MANIFEST_DIRECTORY` you could add a `staticlib` target to your package + manifest as a workaround to make corrosion import the crate. + +* **HEADER_NAME**: The name of the generated header file. This will be the name which you include in your C/C++ code + (e.g. `#include "myproject/myheader.h" if you specify `HEADER_NAME "myproject/myheader.h"`. +* **CBINDGEN_VERSION**: Version requirement for cbindgen. Exact semantics to be specified. Currently not implemented. +* **FLAGS**: Arbitrary other flags for `cbindgen`. Run `cbindgen --help` to see the possible flags. + +[cbindgen]: https://github.com/eqrion/cbindgen + +### Current limitations + +- Cbindgens (optional) macro expansion feature internally actually builds the crate / runs the build script. + For this to work as expected in all cases, we probably need to set all the same environment variables + as when corrosion builds the crate. However the crate is a **library**, so we would need to figure out which + target builds it - and if there are multiple, potentially generate bindings per-target? + Alternatively we could add support of setting some environment variables on rlibs, and pulling that + information in when building the actual corrosion targets + Alternatively we could restrict corrosions support of this feature to actual imported staticlib/cdylib targets. +ANCHOR_END: corrosion_cbindgen +#]=======================================================================] +function(corrosion_experimental_cbindgen) + set(OPTIONS "") + set(ONE_VALUE_KEYWORDS + TARGET + MANIFEST_DIRECTORY + HEADER_NAME + CBINDGEN_VERSION) + set(MULTI_VALUE_KEYWORDS "FLAGS") + cmake_parse_arguments(PARSE_ARGV 0 CCN "${OPTIONS}" "${ONE_VALUE_KEYWORDS}" "${MULTI_VALUE_KEYWORDS}") + + set(required_keywords TARGET HEADER_NAME) + foreach(keyword ${required_keywords}) + if(NOT DEFINED "CCN_${keyword}") + message(FATAL_ERROR "Missing required parameter `${keyword}`.") + elseif("${CCN_${keyword}}" STREQUAL "") + message(FATAL_ERROR "Required parameter `${keyword}` may not be set to an empty string.") + endif() + endforeach() + set(rust_target "${CCN_TARGET}") + unset(package_manifest_dir) + + + set(hostbuild_override "$>") + set(cbindgen_target_triple "$") + + if(TARGET "${rust_target}") + get_target_property(package_manifest_path "${rust_target}" INTERFACE_COR_PACKAGE_MANIFEST_PATH) + if(NOT EXISTS "${package_manifest_path}") + message(FATAL_ERROR "Internal error: No package manifest found at ${package_manifest_path}") + endif() + get_filename_component(package_manifest_dir "${package_manifest_path}" DIRECTORY) + # todo: as an optimization we could cache the cargo metadata output (but --no-deps makes that slightly more complicated) + else() + if(NOT DEFINED CCN_MANIFEST_DIRECTORY) + message(FATAL_ERROR + "`${rust_target}` is not a target imported by corrosion and `MANIFEST_DIRECTORY` was not provided." + ) + else() + set(package_manifest_dir "${CCN_MANIFEST_DIRECTORY}") + endif() + endif() + + get_target_property(rust_cargo_package "${rust_target}" COR_CARGO_PACKAGE_NAME ) + if(NOT rust_cargo_package) + message(FATAL_ERROR "Internal Error: Could not determine cargo package name for cbindgen. " + ) + endif() + message(STATUS "Using package ${rust_cargo_package} as crate for cbindgen") + + + set(output_header_name "${CCN_HEADER_NAME}") + + find_program(installed_cbindgen cbindgen) + + # Install the newest cbindgen version into our build tree. + if(installed_cbindgen) + set(cbindgen "${installed_cbindgen}") + else() + set(local_cbindgen_install_dir "${CMAKE_BINARY_DIR}/corrosion/cbindgen") + unset(executable_postfix) + if(Rust_CARGO_HOST_OS STREQUAL "windows") + set(executable_postfix ".exe") + endif() + set(cbindgen "${local_cbindgen_install_dir}/bin/cbindgen${executable_postfix}") + if(NOT TARGET "_corrosion_cbindgen") + file(MAKE_DIRECTORY "${local_cbindgen_install_dir}") + add_custom_command(OUTPUT "${cbindgen}" + COMMAND ${CMAKE_COMMAND} + -E env + "CARGO_BUILD_RUSTC=${_CORROSION_RUSTC}" + ${_CORROSION_CARGO} install + cbindgen + --root "${local_cbindgen_install_dir}" + ${_CORROSION_QUIET_OUTPUT_FLAG} + COMMENT "Building cbindgen" + ) + add_custom_target("_corrosion_cbindgen" + DEPENDS "${cbindgen}" + ) + endif() + endif() + + set(corrosion_generated_dir "${CMAKE_CURRENT_BINARY_DIR}/corrosion_generated") + set(generated_dir "${corrosion_generated_dir}/cbindgen/${rust_target}") + set(header_placement_dir "${generated_dir}/include/") + set(depfile_placement_dir "${generated_dir}/depfile") + set(generated_depfile "${depfile_placement_dir}/${output_header_name}.d") + set(generated_header "${header_placement_dir}/${output_header_name}") + message(STATUS "rust target is ${rust_target}") + if(CMAKE_VERSION VERSION_GREATER_EQUAL "3.23") + target_sources(${rust_target} + INTERFACE + FILE_SET HEADERS + BASE_DIRS "${header_placement_dir}" + FILES "${header_placement_dir}/${output_header_name}" + ) + else() + # Note: not clear to me how install would best work before CMake 3.23 + target_include_directories(${rust_target} + INTERFACE + $ + $ + ) + endif() + + # This may be different from $header_placement_dir since the user specified HEADER_NAME may contain + # relative directories. + get_filename_component(generated_header_dir "${generated_header}" DIRECTORY) + file(MAKE_DIRECTORY "${generated_header_dir}") + + unset(depfile_cbindgen_arg) + unset(depfile_cmake_arg) + get_filename_component(generated_depfile_dir "${generated_depfile}" DIRECTORY) + file(MAKE_DIRECTORY "${generated_depfile_dir}") + set(depfile_cbindgen_arg "--depfile=${generated_depfile}") + + add_custom_command( + OUTPUT + "${generated_header}" + COMMAND + "${CMAKE_COMMAND}" -E env + TARGET="${cbindgen_target_triple}" + "${cbindgen}" + --output "${generated_header}" + --crate "${rust_cargo_package}" + ${depfile_cbindgen_arg} + ${CCN_FLAGS} + COMMENT "Generate cbindgen bindings for package ${rust_cargo_package} and output header ${generated_header}" + DEPFILE "${generated_depfile}" + COMMAND_EXPAND_LISTS + WORKING_DIRECTORY "${package_manifest_dir}" + ) + + if(NOT installed_cbindgen) + add_custom_command( + OUTPUT "${generated_header}" + APPEND + DEPENDS _corrosion_cbindgen + ) + endif() + + if(NOT TARGET "_corrosion_cbindgen_${rust_target}_bindings") + add_custom_target(_corrosion_cbindgen_${rust_target}_bindings + COMMENT "Generate cbindgen bindings for package ${rust_cargo_package}" + ) + endif() + # Users might want to call cbindgen multiple times, e.g. to generate separate C++ and C header files. + string(MAKE_C_IDENTIFIER "${output_header_name}" header_identifier ) + add_custom_target("_corrosion_cbindgen_${rust_target}_bindings_${header_identifier}" + DEPENDS "${generated_header}" + COMMENT "Generate ${generated_header} for ${rust_target}" + ) + add_dependencies("_corrosion_cbindgen_${rust_target}_bindings" "_corrosion_cbindgen_${rust_target}_bindings_${header_identifier}") + add_dependencies(${rust_target} "_corrosion_cbindgen_${rust_target}_bindings") +endfunction() + +# Parse the version of a Rust package from it's package manifest (Cargo.toml) +function(corrosion_parse_package_version package_manifest_path out_package_version) + if(NOT EXISTS "${package_manifest_path}") + message(FATAL_ERROR "Package manifest `${package_manifest_path}` does not exist.") + endif() + + file(READ "${package_manifest_path}" package_manifest) + + # Find the package table. It may contain arrays, so match until \n\[, which should mark the next + # table. Note: backslashes must be doubled to escape the backslash for the bracket. LF is single + # backslash however. On windows the line also ends in \n, so matching against \n\[ is sufficient + # to detect an opening bracket on a new line. + set(package_table_regex "\\[package\\](.*)\n\\[") + + string(REGEX MATCH "${package_table_regex}" _package_table "${package_manifest}") + + if(CMAKE_MATCH_COUNT EQUAL "1") + set(package_table "${CMAKE_MATCH_1}") + else() + message(DEBUG + "Failed to find `[package]` table in package manifest `${package_manifest_path}`.\n" + "Matches: ${CMAKE_MATCH_COUNT}\n" + ) + set(${out_package_version} + "NOTFOUND" + PARENT_SCOPE + ) + endif() + # Match `version = "0.3.2"`, `"version" = "0.3.2" Contains one matching group for the version + set(version_regex "[\r]?\n[\"']?version[\"']?[ \t]*=[ \t]*[\"']([0-9\.]+)[\"']") + + string(REGEX MATCH "${version_regex}" _version "${package_table}") + + if("${package_table}" MATCHES "${version_regex}") + set(${out_package_version} + "${CMAKE_MATCH_1}" + PARENT_SCOPE + ) + else() + message(DEBUG "Failed to extract package version from manifest `${package_manifest_path}`.") + set(${out_package_version} + "NOTFOUND" + PARENT_SCOPE + ) + endif() +endfunction() + +function(_corrosion_initialize_properties target_name) + # Initialize the `_OUTPUT_DIRECTORY` properties based on `CMAKE__OUTPUT_DIRECTORY`. + foreach(output_var RUNTIME_OUTPUT_DIRECTORY ARCHIVE_OUTPUT_DIRECTORY LIBRARY_OUTPUT_DIRECTORY PDB_OUTPUT_DIRECTORY) + if (DEFINED "CMAKE_${output_var}") + set_property(TARGET ${target_name} PROPERTY "${output_var}" "${CMAKE_${output_var}}") + endif() + + foreach(config_type ${CMAKE_CONFIGURATION_TYPES}) + string(TOUPPER "${config_type}" config_type_upper) + if (DEFINED "CMAKE_${output_var}_${config_type_upper}") + set_property(TARGET ${target_name} PROPERTY "${output_var}_${config_type_upper}" "${CMAKE_${output_var}_${config_type_upper}}") + endif() + endforeach() + endforeach() +endfunction() + +# Helper macro to pass through an optional `OPTION` argument parsed via `cmake_parse_arguments` +# to another function that takes the same OPTION. +# If the option was set, then the variable will be set to the same option name again, +# otherwise will be unset. +macro(_corrosion_option_passthrough_helper option_name prefix var_name) + if(${${prefix}_${option_name}}) + set("${var_name}" "${option_name}") + else() + unset("${var_name}") + endif() +endmacro() + +# Helper macro to pass through an optional argument with value(s), parsed via `cmake_parse_arguments`, +# to another function that takes the same keyword + associated values. +# If the argument was given, then the variable will be a list of the argument name and the values, +# which will be expanded, when calling the function (assuming no quotes). +macro(_corrosion_arg_passthrough_helper arg_name prefix var_name) + if(DEFINED "${prefix}_${arg_name}") + set("${var_name}" "${arg_name}" "${${prefix}_${arg_name}}") + else() + unset("${var_name}") + endif() +endmacro() + +list(POP_BACK CMAKE_MESSAGE_CONTEXT) + diff -r 2b4f361e3891 -r 629d5123a979 tools/corrosion/cmake/CorrosionConfig.cmake.in --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/corrosion/cmake/CorrosionConfig.cmake.in Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,9 @@ +@PACKAGE_INIT@ + +if (Corrosion_FOUND) + return() +endif() + +list(APPEND CMAKE_MODULE_PATH "${PACKAGE_PREFIX_DIR}/@CMAKE_INSTALL_DATADIR@/cmake") + +include(Corrosion) diff -r 2b4f361e3891 -r 629d5123a979 tools/corrosion/cmake/CorrosionGenerator.cmake --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/corrosion/cmake/CorrosionGenerator.cmake Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,338 @@ +function(_cargo_metadata out manifest) + set(OPTIONS LOCKED FROZEN) + set(ONE_VALUE_KEYWORDS "") + set(MULTI_VALUE_KEYWORDS "") + cmake_parse_arguments(PARSE_ARGV 2 CM "${OPTIONS}" "${ONE_VALUE_KEYWORDS}" "${MULTI_VALUE_KEYWORDS}") + + list(APPEND CMAKE_MESSAGE_CONTEXT "_cargo_metadata") + + if(DEFINED CM_UNPARSED_ARGUMENTS) + message(FATAL_ERROR "Internal error - unexpected arguments: ${CM_UNPARSED_ARGUMENTS}") + elseif(DEFINED CM_KEYWORDS_MISSING_VALUES) + message(FATAL_ERROR "Internal error - the following keywords had no associated value(s):" + "${CM_KEYWORDS_MISSING_VALUES}") + endif() + + set(cargo_locked "") + set(cargo_frozen "") + if(LOCKED) + set(cargo_locked "--locked") + endif() + if(FROZEN) + set(cargo_frozen "--frozen") + endif() + execute_process( + COMMAND + ${CMAKE_COMMAND} -E env + "CARGO_BUILD_RUSTC=${_CORROSION_RUSTC}" + "${_CORROSION_CARGO}" + metadata + --manifest-path "${manifest}" + --format-version 1 + # We don't care about non-workspace dependencies + --no-deps + ${cargo_locked} + ${cargo_frozen} + + OUTPUT_VARIABLE json + COMMAND_ERROR_IS_FATAL ANY + ) + + set(${out} "${json}" PARENT_SCOPE) +endfunction() + +# Add targets (crates) of one package +function(_generator_add_package_targets) + set(OPTIONS NO_LINKER_OVERRIDE) + set(ONE_VALUE_KEYWORDS + WORKSPACE_MANIFEST_PATH + PACKAGE_MANIFEST_PATH + PACKAGE_NAME + PACKAGE_VERSION + TARGETS_JSON + OUT_CREATED_TARGETS) + set(MULTI_VALUE_KEYWORDS CRATE_TYPES OVERRIDE_CRATE_TYPE_ARGS) + cmake_parse_arguments(PARSE_ARGV 0 GAPT "${OPTIONS}" "${ONE_VALUE_KEYWORDS}" "${MULTI_VALUE_KEYWORDS}") + + if(DEFINED GAPT_UNPARSED_ARGUMENTS) + message(FATAL_ERROR "Internal error - unexpected arguments: ${GAPT_UNPARSED_ARGUMENTS}") + elseif(DEFINED GAPT_KEYWORDS_MISSING_VALUES) + message(FATAL_ERROR "Internal error - the following keywords had no associated value(s):" + "${GAPT_KEYWORDS_MISSING_VALUES}") + endif() + + _corrosion_option_passthrough_helper(NO_LINKER_OVERRIDE GAPT no_linker_override) + + set(workspace_manifest_path "${GAPT_WORKSPACE_MANIFEST_PATH}") + set(package_manifest_path "${GAPT_PACKAGE_MANIFEST_PATH}") + set(package_name "${GAPT_PACKAGE_NAME}") + set(package_version "${GAPT_PACKAGE_VERSION}") + set(targets "${GAPT_TARGETS_JSON}") + set(out_created_targets "${GAPT_OUT_CREATED_TARGETS}") + set(crate_types "${GAPT_CRATE_TYPES}") + if(DEFINED GAPT_OVERRIDE_CRATE_TYPE_ARGS) + list(GET GAPT_OVERRIDE_CRATE_TYPE_ARGS 0 override_crate_name_list_ref) + list(GET GAPT_OVERRIDE_CRATE_TYPE_ARGS 1 override_crate_types_list_ref) + endif() + + set(corrosion_targets "") + + file(TO_CMAKE_PATH "${package_manifest_path}" manifest_path) + + string(JSON targets_len LENGTH "${targets}") + math(EXPR targets_len-1 "${targets_len} - 1") + + message(DEBUG "Found ${targets_len} targets in package ${package_name}") + + foreach(ix RANGE ${targets_len-1}) + string(JSON target GET "${targets}" ${ix}) + string(JSON target_name GET "${target}" "name") + string(JSON target_kind GET "${target}" "kind") + string(JSON target_kind_len LENGTH "${target_kind}") + + math(EXPR target_kind_len-1 "${target_kind_len} - 1") + set(kinds) + unset(override_package_crate_type) + # OVERRIDE_CRATE_TYPE is more specific than the CRATE_TYPES argument to corrosion_import_crate, and thus takes + # priority. + if(DEFINED GAPT_OVERRIDE_CRATE_TYPE_ARGS) + foreach(override_crate_name override_crate_types IN ZIP_LISTS ${override_crate_name_list_ref} ${override_crate_types_list_ref}) + if("${override_crate_name}" STREQUAL "${target_name}") + message(DEBUG "Forcing crate ${target_name} to crate-type(s): ${override_crate_types}.") + # Convert to CMake list + string(REPLACE "," ";" kinds "${override_crate_types}") + break() + endif() + endforeach() + else() + foreach(ix RANGE ${target_kind_len-1}) + string(JSON kind GET "${target_kind}" ${ix}) + if(NOT crate_types OR ${kind} IN_LIST crate_types) + list(APPEND kinds ${kind}) + endif() + endforeach() + endif() + if(TARGET "${target_name}" + AND ("staticlib" IN_LIST kinds OR "cdylib" IN_LIST kinds OR "bin" IN_LIST kinds) + ) + message(WARNING "Failed to import Rust crate ${target_name} (kind: `${target_kind}`) because a target " + "with the same name already exists. Skipping this target.\n" + "Help: If you are importing a package which exposes both a `lib` and " + "a `bin` target, please consider explicitly naming the targets in your `Cargo.toml` manifest.\n" + "Note: If you have multiple different packages which have targets with the same name, please note that " + "this is currently not supported by Corrosion. Feel free to open an issue on Github to request " + "supporting this scenario." + ) + # Skip this target to prevent a hard error. + continue() + endif() + + if("staticlib" IN_LIST kinds OR "cdylib" IN_LIST kinds) + # Explicitly set library names have always been forbidden from using dashes (by cargo). + # Starting with Rust 1.79, names inherited from the package name will have dashes replaced + # by underscores too. Corrosion will thus replace dashes with underscores, to make the target + # name consistent independent of the Rust version. `bin` target names are not affected. + # See https://github.com/corrosion-rs/corrosion/issues/501 for more details. + string(REPLACE "\-" "_" target_name "${target_name}") + + set(archive_byproducts "") + set(shared_lib_byproduct "") + set(pdb_byproduct "") + + add_library(${target_name} INTERFACE) + _corrosion_initialize_properties(${target_name}) + _corrosion_add_library_target( + WORKSPACE_MANIFEST_PATH "${workspace_manifest_path}" + TARGET_NAME "${target_name}" + LIB_KINDS ${kinds} + OUT_ARCHIVE_OUTPUT_BYPRODUCTS archive_byproducts + OUT_SHARED_LIB_BYPRODUCTS shared_lib_byproduct + OUT_PDB_BYPRODUCT pdb_byproduct + ) + + set(byproducts "") + list(APPEND byproducts "${archive_byproducts}" "${shared_lib_byproduct}" "${pdb_byproduct}") + + set(cargo_build_out_dir "") + _add_cargo_build( + cargo_build_out_dir + PACKAGE ${package_name} + TARGET ${target_name} + MANIFEST_PATH "${manifest_path}" + WORKSPACE_MANIFEST_PATH "${workspace_manifest_path}" + TARGET_KINDS "${kinds}" + BYPRODUCTS "${byproducts}" + # Optional + ${no_linker_override} + ) + if(archive_byproducts) + _corrosion_copy_byproducts( + ${target_name} ARCHIVE_OUTPUT_DIRECTORY "${cargo_build_out_dir}" "${archive_byproducts}" + ) + endif() + if(shared_lib_byproduct) + _corrosion_copy_byproducts( + ${target_name} LIBRARY_OUTPUT_DIRECTORY "${cargo_build_out_dir}" "${shared_lib_byproduct}" + ) + endif() + if(pdb_byproduct) + _corrosion_copy_byproducts( + ${target_name} "PDB_OUTPUT_DIRECTORY;LIBRARY_OUTPUT_DIRECTORY" "${cargo_build_out_dir}" "${pdb_byproduct}" + ) + endif() + list(APPEND corrosion_targets ${target_name}) + set_property(TARGET "${target_name}" PROPERTY COR_CARGO_PACKAGE_NAME "${package_name}" ) + # Note: "bin" is mutually exclusive with "staticlib/cdylib", since `bin`s are seperate crates from libraries. + elseif("bin" IN_LIST kinds) + set(bin_byproduct "") + set(pdb_byproduct "") + add_executable(${target_name} IMPORTED GLOBAL) + _corrosion_initialize_properties(${target_name}) + _corrosion_add_bin_target("${workspace_manifest_path}" "${target_name}" + "bin_byproduct" "pdb_byproduct" + ) + + set(byproducts "") + list(APPEND byproducts "${bin_byproduct}" "${pdb_byproduct}") + + set(cargo_build_out_dir "") + _add_cargo_build( + cargo_build_out_dir + PACKAGE "${package_name}" + TARGET "${target_name}" + MANIFEST_PATH "${manifest_path}" + WORKSPACE_MANIFEST_PATH "${workspace_manifest_path}" + TARGET_KINDS "bin" + BYPRODUCTS "${byproducts}" + # Optional + ${no_linker_override} + ) + _corrosion_copy_byproducts( + ${target_name} RUNTIME_OUTPUT_DIRECTORY "${cargo_build_out_dir}" "${bin_byproduct}" + ) + if(pdb_byproduct) + _corrosion_copy_byproducts( + ${target_name} "PDB_OUTPUT_DIRECTORY;RUNTIME_OUTPUT_DIRECTORY" "${cargo_build_out_dir}" "${pdb_byproduct}" + ) + endif() + list(APPEND corrosion_targets ${target_name}) + set_property(TARGET "${target_name}" PROPERTY COR_CARGO_PACKAGE_NAME "${package_name}" ) + else() + # ignore other kinds (like examples, tests, build scripts, ...) + endif() + endforeach() + + if(NOT corrosion_targets) + message(DEBUG "No relevant targets found in package ${package_name} - Ignoring") + else() + set_target_properties(${corrosion_targets} PROPERTIES INTERFACE_COR_PACKAGE_MANIFEST_PATH "${package_manifest_path}") + endif() + set(${out_created_targets} "${corrosion_targets}" PARENT_SCOPE) + +endfunction() + +# Add all cargo targets defined in the packages defined in the Cargo.toml manifest at +# `MANIFEST_PATH`. +function(_generator_add_cargo_targets) + set(options NO_LINKER_OVERRIDE) + set(one_value_args MANIFEST_PATH IMPORTED_CRATES) + set(multi_value_args CRATES CRATE_TYPES OVERRIDE_CRATE_TYPE_ARGS) + cmake_parse_arguments( + GGC + "${options}" + "${one_value_args}" + "${multi_value_args}" + ${ARGN} + ) + list(APPEND CMAKE_MESSAGE_CONTEXT "_add_cargo_targets") + + _corrosion_option_passthrough_helper(NO_LINKER_OVERRIDE GGC no_linker_override) + _corrosion_arg_passthrough_helper(CRATE_TYPES GGC crate_types) + _corrosion_arg_passthrough_helper(OVERRIDE_CRATE_TYPE_ARGS GGC override_crate_types) + + _cargo_metadata(json "${GGC_MANIFEST_PATH}") + string(JSON packages GET "${json}" "packages") + string(JSON workspace_members GET "${json}" "workspace_members") + + string(JSON pkgs_len LENGTH "${packages}") + math(EXPR pkgs_len-1 "${pkgs_len} - 1") + + string(JSON ws_mems_len LENGTH ${workspace_members}) + math(EXPR ws_mems_len-1 "${ws_mems_len} - 1") + + set(created_targets "") + set(available_package_names "") + foreach(ix RANGE ${pkgs_len-1}) + string(JSON pkg GET "${packages}" ${ix}) + string(JSON pkg_id GET "${pkg}" "id") + string(JSON pkg_name GET "${pkg}" "name") + string(JSON pkg_manifest_path GET "${pkg}" "manifest_path") + string(JSON pkg_version GET "${pkg}" "version") + list(APPEND available_package_names "${pkg_name}") + + if(DEFINED GGC_CRATES) + if(NOT pkg_name IN_LIST GGC_CRATES) + continue() + endif() + endif() + + # probably this loop is not necessary at all, since when using --no-deps, the + # contents of packages should already be only workspace members! + unset(pkg_is_ws_member) + foreach(ix RANGE ${ws_mems_len-1}) + string(JSON ws_mem GET "${workspace_members}" ${ix}) + if(ws_mem STREQUAL pkg_id) + set(pkg_is_ws_member YES) + break() + endif() + endforeach() + + if(NOT DEFINED pkg_is_ws_member) + # Since we pass `--no-deps` to cargo metadata now, I think this situation can't happen, but lets check for + # it anyway, just to discover any potential issues. + # If nobody complains for a while, it should be safe to remove this check and the previous loop, which + # should speed up the configuration process. + message(WARNING "The package `${pkg_name}` unexpectedly is not part of the workspace." + "Please open an issue at corrosion with some background information on the package" + ) + endif() + + string(JSON targets GET "${pkg}" "targets") + + _generator_add_package_targets( + WORKSPACE_MANIFEST_PATH "${GGC_MANIFEST_PATH}" + PACKAGE_MANIFEST_PATH "${pkg_manifest_path}" + PACKAGE_NAME "${pkg_name}" + PACKAGE_VERSION "${pkg_version}" + TARGETS_JSON "${targets}" + OUT_CREATED_TARGETS curr_created_targets + ${no_linker_override} + ${crate_types} + ${override_crate_types} + ) + list(APPEND created_targets "${curr_created_targets}") + endforeach() + + if(NOT created_targets) + set(crates_error_message "") + if(DEFINED GGC_CRATES) + set(crates_error_message "\n`corrosion_import_crate()` was called with the `CRATES` " + "parameter set to `${GGC_CRATES}`. Corrosion will only attempt to import packages matching " + "names from this list." + ) + endif() + message(FATAL_ERROR + "Found no targets in ${pkgs_len} packages." + ${crates_error_message}. + "\nPlease keep in mind that corrosion will only import Rust `bin` targets or" + "`staticlib` or `cdylib` library targets." + "The following packages were found in the Manifest: ${available_package_names}" + ) + else() + message(DEBUG "Corrosion created the following CMake targets: ${created_targets}") + endif() + + if(GGC_IMPORTED_CRATES) + set(${GGC_IMPORTED_CRATES} "${created_targets}" PARENT_SCOPE) + endif() +endfunction() diff -r 2b4f361e3891 -r 629d5123a979 tools/corrosion/cmake/FindRust.cmake --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/corrosion/cmake/FindRust.cmake Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,900 @@ +#[=======================================================================[.rst: +FindRust +-------- + +Find Rust + +This module finds an installed rustc compiler and the cargo build tool. If Rust +is managed by rustup it determines the available toolchains and returns a +concrete Rust version, not a rustup proxy. + +#]=======================================================================] + +cmake_minimum_required(VERSION 3.12) + +option( + Rust_RUSTUP_INSTALL_MISSING_TARGET + "Use Rustup to automatically install missing targets instead of giving up" + OFF +) + +# search for Cargo here and set up a bunch of cool flags and stuff +include(FindPackageHandleStandardArgs) + +list(APPEND CMAKE_MESSAGE_CONTEXT "FindRust") + +# Print error message and return. Should not be used from inside functions +macro(_findrust_failed) + if("${Rust_FIND_REQUIRED}") + message(FATAL_ERROR ${ARGN}) + elseif(NOT "${Rust_FIND_QUIETLY}") + message(WARNING ${ARGN}) + endif() + set(Rust_FOUND "") + return() +endmacro() + +# Checks if the actual version of a Rust toolchain matches the VERSION requirements specified in find_package. +function(_findrust_version_ok ACTUAL_VERSION OUT_IS_OK) + if(DEFINED Rust_FIND_VERSION_RANGE) + if(Rust_FIND_VERSION_RANGE_MAX STREQUAL "INCLUDE") + set(COMPARSION_OPERATOR "VERSION_LESS_EQUAL") + elseif(Rust_FIND_VERSION_RANGE_MAX STREQUAL "EXCLUDE") + set(COMPARSION_OPERATOR "VERSION_LESS") + else() + message(FATAL_ERROR "Unexpected value in `_FIND_VERSION_RANGE_MAX`: " + "`${Rust_FIND_VERSION_RANGE_MAX}`.") + endif() + if(("${ACTUAL_VERSION}" VERSION_GREATER_EQUAL "${Rust_FIND_VERSION_RANGE_MIN}") + AND + ( "${ACTUAL_VERSION}" ${COMPARSION_OPERATOR} "${Rust_FIND_VERSION_RANGE_MAX}" ) + ) + set("${OUT_IS_OK}" TRUE PARENT_SCOPE) + else() + set("${OUT_IS_OK}" FALSE PARENT_SCOPE) + endif() + elseif(DEFINED Rust_FIND_VERSION) + if(Rust_VERSION_EXACT) + set(COMPARISON_OPERATOR VERSION_EQUAL) + else() + set(COMPARISON_OPERATOR VERSION_GREATER_EQUAL) + endif() + if(_TOOLCHAIN_${_TOOLCHAIN_SELECTED}_VERSION "${COMPARISON_OPERATOR}" Rust_FIND_VERSION) + set("${OUT_IS_OK}" TRUE PARENT_SCOPE) + else() + set("${OUT_IS_OK}" FALSE PARENT_SCOPE) + endif() + else() + # if no VERSION requirement was specified, the version is always okay. + set("${OUT_IS_OK}" TRUE PARENT_SCOPE) + endif() +endfunction() + +function(_corrosion_strip_target_triple input_triple_or_path output_triple) + # If the target_triple is a path to a custom target specification file, then strip everything + # except the filename from `target_triple`. + get_filename_component(target_triple_ext "${input_triple_or_path}" EXT) + set(target_triple "${input_triple_or_path}") + if(target_triple_ext) + if(target_triple_ext STREQUAL ".json") + get_filename_component(target_triple "${input_triple_or_path}" NAME_WE) + endif() + endif() + set(${output_triple} "${target_triple}" PARENT_SCOPE) +endfunction() + +function(_corrosion_parse_target_triple target_triple out_arch out_vendor out_os out_env) + _corrosion_strip_target_triple(${target_triple} target_triple) + + # The vendor part may be left out from the target triple, and since `env` is also optional, + # we determine if vendor is present by matching against a list of known vendors. + set(known_vendors + "apple" + "esp[a-z0-9]*" # espressif, e.g. riscv32imc-esp-espidf or xtensa-esp32s3-none-elf + "fortanix" + "kmc" + "pc" + "nintendo" + "nvidia" + "openwrt" + "alpine" + "chimera" + "unikraft" + "unknown" + "uwp" # aarch64-uwp-windows-msvc + "wrs" # e.g. aarch64-wrs-vxworks + "sony" + "sun" + ) + # todo: allow users to add additional vendors to the list via a cmake variable. + list(JOIN known_vendors "|" known_vendors_joined) + # vendor is optional - We detect if vendor is present by matching against a known list of + # vendors. The next field is the OS, which we assume to always be present, while the last field + # is again optional and contains the environment. + string(REGEX MATCH + "^([a-z0-9_\.]+)-((${known_vendors_joined})-)?([a-z0-9_]+)(-([a-z0-9_]+))?$" + whole_match + "${target_triple}" + ) + if((NOT whole_match) AND (NOT CORROSION_NO_WARN_PARSE_TARGET_TRIPLE_FAILED)) + message(WARNING "Failed to parse target-triple `${target_triple}`." + "Corrosion determines some information about the output artifacts based on OS " + "specified in the Rust target-triple.\n" + "Currently this is relevant for windows and darwin (mac) targets, since file " + "extensions differ.\n" + "Note: If you are targeting a different OS you can suppress this warning by" + " setting the CMake cache variable " + "`CORROSION_NO_WARN_PARSE_TARGET_TRIPLE_FAILED`." + "Please consider opening an issue on github if you you need to add a new vendor to the list." + ) + endif() + + message(DEBUG "Parsed Target triple: arch: ${CMAKE_MATCH_1}, vendor: ${CMAKE_MATCH_3}, " + "OS: ${CMAKE_MATCH_4}, env: ${CMAKE_MATCH_6}") + + set("${out_arch}" "${CMAKE_MATCH_1}" PARENT_SCOPE) + set("${out_vendor}" "${CMAKE_MATCH_3}" PARENT_SCOPE) + set("${out_os}" "${CMAKE_MATCH_4}" PARENT_SCOPE) + set("${out_env}" "${CMAKE_MATCH_6}" PARENT_SCOPE) +endfunction() + +function(_corrosion_determine_libs_new target_triple out_libs out_flags) + set(package_dir "${CMAKE_BINARY_DIR}/corrosion/required_libs") + # Cleanup on reconfigure to get a cleans state (in case we change something in the future) + file(REMOVE_RECURSE "${package_dir}") + file(MAKE_DIRECTORY "${package_dir}") + set(manifest "[package]\nname = \"required_libs\"\nedition = \"2018\"\nversion = \"0.1.0\"\n") + string(APPEND manifest "\n[lib]\ncrate-type=[\"staticlib\"]\npath = \"lib.rs\"\n") + string(APPEND manifest "\n[workspace]\n") + file(WRITE "${package_dir}/Cargo.toml" "${manifest}") + file(WRITE "${package_dir}/lib.rs" "pub fn add(left: usize, right: usize) -> usize {left + right}\n") + + execute_process( + COMMAND ${CMAKE_COMMAND} -E env + "CARGO_BUILD_RUSTC=${Rust_COMPILER_CACHED}" + ${Rust_CARGO_CACHED} rustc --verbose --color never --target=${target_triple} -- --print=native-static-libs + WORKING_DIRECTORY "${CMAKE_BINARY_DIR}/corrosion/required_libs" + RESULT_VARIABLE cargo_build_result + ERROR_VARIABLE cargo_build_error_message + ) + if(cargo_build_result) + message(DEBUG "Determining required native libraries - failed: ${cargo_build_result}.") + message(TRACE "The cargo build error was: ${cargo_build_error_message}") + message(DEBUG "Note: This is expected for Rust targets without std support") + return() + else() + # The pattern starts with `native-static-libs:` and goes to the end of the line. + if(cargo_build_error_message MATCHES "native-static-libs: ([^\r\n]+)\r?\n") + string(REPLACE " " ";" "libs_list" "${CMAKE_MATCH_1}") + set(stripped_lib_list "") + set(flag_list "") + + set(was_last_framework OFF) + foreach(lib ${libs_list}) + # merge -framework;lib -> "-framework lib" as CMake does de-duplication of link libraries, and -framework prefix is required + if (lib STREQUAL "-framework") + set(was_last_framework ON) + continue() + endif() + if (was_last_framework) + list(APPEND stripped_lib_list "-framework ${lib}") + set(was_last_framework OFF) + continue() + endif() + + # Flags start with / for MSVC + if (lib MATCHES "^/" AND ${target_triple} MATCHES "msvc$") + # Windows GNU uses the compiler to invoke the linker, so -Wl, prefix is needed + # https://gitlab.kitware.com/cmake/cmake/-/blob/9bed4f4d817f139f0c2e050d7420e1e247949fe4/Modules/Platform/Windows-GNU.cmake#L156 + if (CMAKE_CXX_COMPILER_FRONTEND_VARIANT STREQUAL "GNU") + list(APPEND flag_list "-Wl,${lib}") + else() + list(APPEND flag_list "${lib}") + endif() + else() + # Strip leading `-l` (unix) and potential .lib suffix (windows) + string(REGEX REPLACE "^-l" "" "stripped_lib" "${lib}") + string(REGEX REPLACE "\.lib$" "" "stripped_lib" "${stripped_lib}") + list(APPEND stripped_lib_list "${stripped_lib}") + endif() + endforeach() + set(libs_list "${stripped_lib_list}") + # We leave it up to the C/C++ executable that links in the Rust static-library + # to determine which version of the msvc runtime library it should select. + list(FILTER libs_list EXCLUDE REGEX "^msvcrtd?") + list(FILTER flag_list EXCLUDE REGEX "^(-Wl,)?/defaultlib:msvcrtd?") + else() + message(DEBUG "Determining required native libraries - failed: Regex match failure.") + message(DEBUG "`native-static-libs` not found in: `${cargo_build_error_message}`") + return() + endif() + endif() + set("${out_libs}" "${libs_list}" PARENT_SCOPE) + set("${out_flags}" "${flag_list}" PARENT_SCOPE) +endfunction() + +if (NOT "${Rust_TOOLCHAIN}" STREQUAL "$CACHE{Rust_TOOLCHAIN}") + # Promote Rust_TOOLCHAIN to a cache variable if it is not already a cache variable + set(Rust_TOOLCHAIN ${Rust_TOOLCHAIN} CACHE STRING "Requested rustup toolchain" FORCE) +endif() + +set(_RESOLVE_RUSTUP_TOOLCHAINS_DESC "Indicates whether to descend into the toolchain pointed to by rustup") +set(Rust_RESOLVE_RUSTUP_TOOLCHAINS ON CACHE BOOL ${_RESOLVE_RUSTUP_TOOLCHAINS_DESC}) + +# This block checks to see if we're prioritizing a rustup-managed toolchain. +if (DEFINED Rust_TOOLCHAIN) + # If the user specifies `Rust_TOOLCHAIN`, then look for `rustup` first, rather than `rustc`. + find_program(Rust_RUSTUP rustup PATHS "$ENV{HOME}/.cargo/bin") + if(NOT Rust_RUSTUP) + if(NOT "${Rust_FIND_QUIETLY}") + message( + WARNING "CMake variable `Rust_TOOLCHAIN` specified, but `rustup` was not found. " + "Ignoring toolchain and looking for a Rust toolchain not managed by rustup.") + endif() + endif() +else() + # If we aren't definitely using a rustup toolchain, look for rustc first - the user may have + # a toolchain installed via a method other than rustup higher in the PATH, which should be + # preferred. However, if the first-found rustc is a rustup proxy, then we'll revert to + # finding the preferred toolchain via rustup. + + # Uses `Rust_COMPILER` to let user-specified `rustc` win. But we will still "override" the + # user's setting if it is pointing to `rustup`. Default rustup install path is provided as a + # backup if a toolchain cannot be found in the user's PATH. + + if (DEFINED Rust_COMPILER) + set(_Rust_COMPILER_TEST "${Rust_COMPILER}") + set(_USER_SPECIFIED_RUSTC ON) + if(NOT (EXISTS "${_Rust_COMPILER_TEST}" AND NOT IS_DIRECTORY "${_Rust_COMPILER_TEST}")) + set(_ERROR_MESSAGE "Rust_COMPILER was set to `${Rust_COMPILER}`, but this file does " + "not exist." + ) + _findrust_failed(${_ERROR_MESSAGE}) + return() + endif() + else() + find_program(_Rust_COMPILER_TEST rustc PATHS "$ENV{HOME}/.cargo/bin") + if(NOT EXISTS "${_Rust_COMPILER_TEST}") + cmake_path(CONVERT "$ENV{HOME}/.cargo/bin" TO_CMAKE_PATH_LIST _cargo_bin_dir) + set(_ERROR_MESSAGE "`rustc` not found in PATH or `${_cargo_bin_dir}`.\n" + "Hint: Check if `rustc` is in PATH or manually specify the location " + "by setting `Rust_COMPILER` to the path to `rustc`.") + _findrust_failed(${_ERROR_MESSAGE}) + endif() + endif() + + # Check if the discovered rustc is actually a "rustup" proxy. + execute_process( + COMMAND + ${CMAKE_COMMAND} -E env + RUSTUP_FORCE_ARG0=rustup + "${_Rust_COMPILER_TEST}" --version + OUTPUT_VARIABLE _RUSTC_VERSION_RAW + ERROR_VARIABLE _RUSTC_VERSION_STDERR + RESULT_VARIABLE _RUSTC_VERSION_RESULT + ) + + if(NOT (_RUSTC_VERSION_RESULT EQUAL "0")) + _findrust_failed("`${_Rust_COMPILER_TEST} --version` failed with ${_RUSTC_VERSION_RESULT}\n" + "rustc stderr:\n${_RUSTC_VERSION_STDERR}" + ) + endif() + + if (_RUSTC_VERSION_RAW MATCHES "rustup [0-9\\.]+") + if (_USER_SPECIFIED_RUSTC) + message( + WARNING "User-specified Rust_COMPILER pointed to rustup's rustc proxy. Corrosion's " + "FindRust will always try to evaluate to an actual Rust toolchain, and so the " + "user-specified Rust_COMPILER will be discarded in favor of the default " + "rustup-managed toolchain." + ) + + unset(Rust_COMPILER) + unset(Rust_COMPILER CACHE) + endif() + + # Get `rustup` next to the `rustc` proxy + get_filename_component(_RUST_PROXIES_PATH "${_Rust_COMPILER_TEST}" DIRECTORY) + find_program(Rust_RUSTUP rustup HINTS "${_RUST_PROXIES_PATH}" NO_DEFAULT_PATH) + endif() + + unset(_Rust_COMPILER_TEST CACHE) +endif() + +# At this point, the only thing we should have evaluated is a path to `rustup` _if that's what the +# best source for a Rust toolchain was determined to be_. +if (NOT Rust_RUSTUP) + set(Rust_RESOLVE_RUSTUP_TOOLCHAINS OFF CACHE BOOL ${_RESOLVE_RUSTUP_TOOLCHAINS_DESC} FORCE) +endif() + +# List of user variables that will override any toolchain-provided setting +set(_Rust_USER_VARS Rust_COMPILER Rust_CARGO Rust_CARGO_TARGET Rust_CARGO_HOST_TARGET) +foreach(_VAR ${_Rust_USER_VARS}) + if (DEFINED "${_VAR}") + set(${_VAR}_CACHED "${${_VAR}}" CACHE INTERNAL "Internal cache of ${_VAR}") + else() + unset(${_VAR}_CACHED CACHE) + endif() +endforeach() + +# Discover what toolchains are installed by rustup, if the discovered `rustc` is a proxy from +# `rustup` and the user hasn't explicitly requested to override this behavior, then select either +# the default toolchain, or the requested toolchain Rust_TOOLCHAIN +if (Rust_RESOLVE_RUSTUP_TOOLCHAINS) + execute_process( + COMMAND + "${Rust_RUSTUP}" toolchain list --verbose + OUTPUT_VARIABLE _TOOLCHAINS_RAW + ) + + string(REPLACE "\n" ";" _TOOLCHAINS_RAW "${_TOOLCHAINS_RAW}") + set(_DISCOVERED_TOOLCHAINS "") + set(_DISCOVERED_TOOLCHAINS_RUSTC_PATH "") + set(_DISCOVERED_TOOLCHAINS_CARGO_PATH "") + set(_DISCOVERED_TOOLCHAINS_VERSION "") + + foreach(_TOOLCHAIN_RAW ${_TOOLCHAINS_RAW}) + if (_TOOLCHAIN_RAW MATCHES "([a-zA-Z0-9\\._\\-]+)[ \t\r\n]?(\\(default\\) \\(override\\)|\\(default\\)|\\(override\\))?[ \t\r\n]+(.+)") + set(_TOOLCHAIN "${CMAKE_MATCH_1}") + set(_TOOLCHAIN_TYPE "${CMAKE_MATCH_2}") + + set(_TOOLCHAIN_PATH "${CMAKE_MATCH_3}") + set(_TOOLCHAIN_${_TOOLCHAIN}_PATH "${CMAKE_MATCH_3}") + + if (_TOOLCHAIN_TYPE MATCHES ".*\\(default\\).*") + set(_TOOLCHAIN_DEFAULT "${_TOOLCHAIN}") + endif() + + if (_TOOLCHAIN_TYPE MATCHES ".*\\(override\\).*") + set(_TOOLCHAIN_OVERRIDE "${_TOOLCHAIN}") + endif() + + execute_process( + COMMAND + "${_TOOLCHAIN_PATH}/bin/rustc" --version + OUTPUT_VARIABLE _TOOLCHAIN_RAW_VERSION + ) + if (_TOOLCHAIN_RAW_VERSION MATCHES "rustc ([0-9]+)\\.([0-9]+)\\.([0-9]+)(-nightly)?") + list(APPEND _DISCOVERED_TOOLCHAINS "${_TOOLCHAIN}") + list(APPEND _DISCOVERED_TOOLCHAINS_RUSTC_PATH "${_TOOLCHAIN_PATH}/bin/rustc") + list(APPEND _DISCOVERED_TOOLCHAINS_VERSION "${CMAKE_MATCH_1}.${CMAKE_MATCH_2}.${CMAKE_MATCH_3}") + + # We need this variable to determine the default toolchain, since `foreach(... IN ZIP_LISTS ...)` + # requires CMake 3.17. As a workaround we define this variable to lookup the version when iterating + # through the `_DISCOVERED_TOOLCHAINS` lists. + set(_TOOLCHAIN_${_TOOLCHAIN}_VERSION "${CMAKE_MATCH_1}.${CMAKE_MATCH_2}.${CMAKE_MATCH_3}") + if(CMAKE_MATCH_4) + set(_TOOLCHAIN_${_TOOLCHAIN}_IS_NIGHTLY "TRUE") + else() + set(_TOOLCHAIN_${_TOOLCHAIN}_IS_NIGHTLY "FALSE") + endif() + if(EXISTS "${_TOOLCHAIN_PATH}/bin/cargo") + list(APPEND _DISCOVERED_TOOLCHAINS_CARGO_PATH "${_TOOLCHAIN_PATH}/bin/cargo") + else() + list(APPEND _DISCOVERED_TOOLCHAINS_CARGO_PATH "NOTFOUND") + endif() + else() + message(AUTHOR_WARNING "Unexpected output from `rustc --version` for Toolchain `${_TOOLCHAIN}`: " + "`${_TOOLCHAIN_RAW_VERSION}`.\n" + "Ignoring this toolchain." + ) + endif() + else() + message(AUTHOR_WARNING "Didn't recognize toolchain: ${_TOOLCHAIN_RAW}. Ignoring this toolchain.\n" + "Rustup toolchain list output( `${Rust_RUSTUP} toolchain list --verbose`):\n" + "${_TOOLCHAINS_RAW}" + ) + endif() + endforeach() + + # Expose a list of available rustup toolchains. + list(LENGTH _DISCOVERED_TOOLCHAINS _toolchain_len) + list(LENGTH _DISCOVERED_TOOLCHAINS_RUSTC_PATH _toolchain_rustc_len) + list(LENGTH _DISCOVERED_TOOLCHAINS_CARGO_PATH _toolchain_cargo_len) + list(LENGTH _DISCOVERED_TOOLCHAINS_VERSION _toolchain_version_len) + if(NOT + (_toolchain_len EQUAL _toolchain_rustc_len + AND _toolchain_cargo_len EQUAL _toolchain_version_len + AND _toolchain_len EQUAL _toolchain_cargo_len) + ) + message(FATAL_ERROR "Internal error - list length mismatch." + "List lengths: ${_toolchain_len} toolchains, ${_toolchain_rustc_len} rustc, ${_toolchain_cargo_len} cargo," + " ${_toolchain_version_len} version. The lengths should be the same." + ) + endif() + + set(Rust_RUSTUP_TOOLCHAINS "${_DISCOVERED_TOOLCHAINS}" CACHE INTERNAL "List of available Rustup toolchains") + set(Rust_RUSTUP_TOOLCHAINS_RUSTC_PATH "${_DISCOVERED_TOOLCHAINS_RUSTC_PATH}" + CACHE INTERNAL + "List of the rustc paths corresponding to the toolchain at the same index in `Rust_RUSTUP_TOOLCHAINS`." + ) + set(Rust_RUSTUP_TOOLCHAINS_CARGO_PATH "${_DISCOVERED_TOOLCHAINS_CARGO_PATH}" + CACHE INTERNAL + "List of the cargo paths corresponding to the toolchain at the same index in `Rust_RUSTUP_TOOLCHAINS`. \ + May also be `NOTFOUND` if the toolchain does not have a cargo executable." + ) + set(Rust_RUSTUP_TOOLCHAINS_VERSION "${_DISCOVERED_TOOLCHAINS_VERSION}" + CACHE INTERNAL + "List of the rust toolchain version corresponding to the toolchain at the same index in \ + `Rust_RUSTUP_TOOLCHAINS`." + ) + + # Rust_TOOLCHAIN is preferred over a requested version if it is set. + if (NOT DEFINED Rust_TOOLCHAIN) + if (NOT DEFINED _TOOLCHAIN_OVERRIDE) + set(_TOOLCHAIN_SELECTED "${_TOOLCHAIN_DEFAULT}") + else() + set(_TOOLCHAIN_SELECTED "${_TOOLCHAIN_OVERRIDE}") + endif() + # Check default toolchain first. + _findrust_version_ok("_TOOLCHAIN_${_TOOLCHAIN_SELECTED}_VERSION" _VERSION_OK) + if(NOT "${_VERSION_OK}") + foreach(_TOOLCHAIN "${_DISCOVERED_TOOLCHAINS}") + _findrust_version_ok("_TOOLCHAIN_${_TOOLCHAIN}_VERSION" _VERSION_OK) + if("${_VERSION_OK}") + set(_TOOLCHAIN_SELECTED "${_TOOLCHAIN}") + break() + endif() + endforeach() + # Check if we found a suitable version in the for loop. + if(NOT "${_VERSION_OK}") + string(REPLACE ";" "\n" _DISCOVERED_TOOLCHAINS "${_DISCOVERED_TOOLCHAINS}") + _findrust_failed("Failed to find a Rust toolchain matching the version requirements of " + "${Rust_FIND_VERSION}. Available toolchains: ${_DISCOVERED_TOOLCHAINS}") + endif() + endif() + endif() + + set(Rust_TOOLCHAIN "${_TOOLCHAIN_SELECTED}" CACHE STRING "The rustup toolchain to use") + set_property(CACHE Rust_TOOLCHAIN PROPERTY STRINGS "${_DISCOVERED_TOOLCHAINS}") + + if(NOT Rust_FIND_QUIETLY) + message(STATUS "Rust Toolchain: ${Rust_TOOLCHAIN}") + endif() + + if (NOT Rust_TOOLCHAIN IN_LIST _DISCOVERED_TOOLCHAINS) + # If the precise toolchain wasn't found, try appending the default host + execute_process( + COMMAND + "${Rust_RUSTUP}" show + RESULT_VARIABLE _SHOW_RESULT + OUTPUT_VARIABLE _SHOW_RAW + ) + if(NOT "${_SHOW_RESULT}" EQUAL "0") + _findrust_failed("Command `${Rust_RUSTUP} show` failed") + endif() + + if (_SHOW_RAW MATCHES "Default host: ([a-zA-Z0-9_\\-]*)\n") + set(_DEFAULT_HOST "${CMAKE_MATCH_1}") + else() + _findrust_failed("Failed to parse \"Default host\" from `${Rust_RUSTUP} show`. Got: ${_SHOW_RAW}") + endif() + + if (NOT "${Rust_TOOLCHAIN}-${_DEFAULT_HOST}" IN_LIST _DISCOVERED_TOOLCHAINS) + set(_NOT_FOUND_MESSAGE "Could not find toolchain '${Rust_TOOLCHAIN}'\n" + "Available toolchains:\n" + ) + foreach(_TOOLCHAIN ${_DISCOVERED_TOOLCHAINS}) + list(APPEND _NOT_FOUND_MESSAGE " `${_TOOLCHAIN}`\n") + endforeach() + _findrust_failed(${_NOT_FOUND_MESSAGE}) + endif() + + set(_RUSTUP_TOOLCHAIN_FULL "${Rust_TOOLCHAIN}-${_DEFAULT_HOST}") + else() + set(_RUSTUP_TOOLCHAIN_FULL "${Rust_TOOLCHAIN}") + endif() + + set(_RUST_TOOLCHAIN_PATH "${_TOOLCHAIN_${_RUSTUP_TOOLCHAIN_FULL}_PATH}") + if(NOT "${Rust_FIND_QUIETLY}") + message(VERBOSE "Rust toolchain ${_RUSTUP_TOOLCHAIN_FULL}") + message(VERBOSE "Rust toolchain path ${_RUST_TOOLCHAIN_PATH}") + endif() + + # Is overridden if the user specifies `Rust_COMPILER` explicitly. + find_program( + Rust_COMPILER_CACHED + rustc + HINTS "${_RUST_TOOLCHAIN_PATH}/bin" + NO_DEFAULT_PATH) +else() + message(DEBUG "Rust_RESOLVE_RUSTUP_TOOLCHAINS=OFF and Rust_RUSTUP=${Rust_RUSTUP}") + if(Rust_RUSTUP) + get_filename_component(_RUSTUP_DIR "${Rust_RUSTUP}" DIRECTORY) + find_program(Rust_COMPILER_CACHED rustc HINTS "${_RUSTUP_DIR}") + else() + find_program(Rust_COMPILER_CACHED rustc) + endif() + message(DEBUG "find_program rustc: ${Rust_COMPILER_CACHED}") + if (EXISTS "${Rust_COMPILER_CACHED}") + # rustc is expected to be at `/bin/rustc`. + get_filename_component(_RUST_TOOLCHAIN_PATH "${Rust_COMPILER_CACHED}" DIRECTORY) + get_filename_component(_RUST_TOOLCHAIN_PATH "${_RUST_TOOLCHAIN_PATH}" DIRECTORY) + endif() +endif() + +if (NOT EXISTS "${Rust_COMPILER_CACHED}") + set(_NOT_FOUND_MESSAGE "The rustc executable was not found. " + "Rust not installed or ~/.cargo/bin not added to path?\n" + "Hint: Consider setting `Rust_COMPILER` to the absolute path of `rustc`." + ) + _findrust_failed(${_NOT_FOUND_MESSAGE}) +endif() + +if (Rust_RESOLVE_RUSTUP_TOOLCHAINS) + set(_NOT_FOUND_MESSAGE "Rust was detected to be managed by rustup, but failed to find `cargo` " + "next to `rustc` in `${_RUST_TOOLCHAIN_PATH}/bin`. This can happen for custom toolchains, " + "if cargo was not built. " + "Please manually specify the path to a compatible `cargo` by setting `Rust_CARGO`." + ) + find_program( + Rust_CARGO_CACHED + cargo + HINTS "${_RUST_TOOLCHAIN_PATH}/bin" + NO_DEFAULT_PATH + ) + # note: maybe can use find_package_handle_standard_args here, if we remove the _CACHED postfix. + # not sure why that is here... + if(NOT EXISTS "${Rust_CARGO_CACHED}") + _findrust_failed(${_NOT_FOUND_MESSAGE}) + endif() + set(Rust_TOOLCHAIN_IS_RUSTUP_MANAGED TRUE CACHE INTERNAL "" FORCE) +else() + set(_NOT_FOUND_MESSAGE "Failed to find `cargo` in PATH and `${_RUST_TOOLCHAIN_PATH}/bin`.\n" + "Please ensure cargo is in PATH or manually specify the path to a compatible `cargo` by " + "setting `Rust_CARGO`." + ) + # On some systems (e.g. NixOS) cargo is not managed by rustup and also not next to rustc. + find_program( + Rust_CARGO_CACHED + cargo + HINTS "${_RUST_TOOLCHAIN_PATH}/bin" + ) + # note: maybe can use find_package_handle_standard_args here, if we remove the _CACHED postfix. + # not sure why that is here... + if(NOT EXISTS "${Rust_CARGO_CACHED}") + _findrust_failed(${_NOT_FOUND_MESSAGE}) + endif() +endif() + +execute_process( + COMMAND "${Rust_CARGO_CACHED}" --version --verbose + OUTPUT_VARIABLE _CARGO_VERSION_RAW + RESULT_VARIABLE _CARGO_VERSION_RESULT +) +# todo: check if cargo is a required component! +if(NOT ( "${_CARGO_VERSION_RESULT}" EQUAL "0" )) + _findrust_failed("Failed to get cargo version.\n" + "`${Rust_CARGO_CACHED} --version` failed with error: `${_CARGO_VERSION_RESULT}" +) +endif() + +# todo: don't set cache variables here, but let find_package_handle_standard_args do the promotion +# later. +if (_CARGO_VERSION_RAW MATCHES "cargo ([0-9]+)\\.([0-9]+)\\.([0-9]+)") + set(Rust_CARGO_VERSION_MAJOR "${CMAKE_MATCH_1}" CACHE INTERNAL "" FORCE) + set(Rust_CARGO_VERSION_MINOR "${CMAKE_MATCH_2}" CACHE INTERNAL "" FORCE) + set(Rust_CARGO_VERSION_PATCH "${CMAKE_MATCH_3}" CACHE INTERNAL "" FORCE) + set(Rust_CARGO_VERSION "${Rust_CARGO_VERSION_MAJOR}.${Rust_CARGO_VERSION_MINOR}.${Rust_CARGO_VERSION_PATCH}" CACHE INTERNAL "" FORCE) +# Workaround for the version strings where the `cargo ` prefix is missing. +elseif(_CARGO_VERSION_RAW MATCHES "([0-9]+)\\.([0-9]+)\\.([0-9]+)") + set(Rust_CARGO_VERSION_MAJOR "${CMAKE_MATCH_1}" CACHE INTERNAL "" FORCE) + set(Rust_CARGO_VERSION_MINOR "${CMAKE_MATCH_2}" CACHE INTERNAL "" FORCE) + set(Rust_CARGO_VERSION_PATCH "${CMAKE_MATCH_3}" CACHE INTERNAL "" FORCE) + set(Rust_CARGO_VERSION "${Rust_CARGO_VERSION_MAJOR}.${Rust_CARGO_VERSION_MINOR}.${Rust_CARGO_VERSION_PATCH}" CACHE INTERNAL "" FORCE) +else() + _findrust_failed( + "Failed to parse cargo version. `cargo --version` evaluated to (${_CARGO_VERSION_RAW}). " + "Expected a .. version triple." + ) +endif() + +execute_process( + COMMAND "${Rust_COMPILER_CACHED}" --version --verbose + OUTPUT_VARIABLE _RUSTC_VERSION_RAW + RESULT_VARIABLE _RUSTC_VERSION_RESULT +) + +if(NOT ( "${_RUSTC_VERSION_RESULT}" EQUAL "0" )) + _findrust_failed("Failed to get rustc version.\n" + "${Rust_COMPILER_CACHED} --version failed with error: `${_RUSTC_VERSION_RESULT}`") +endif() + +if (_RUSTC_VERSION_RAW MATCHES "rustc ([0-9]+)\\.([0-9]+)\\.([0-9]+)(-nightly)?") + set(Rust_VERSION_MAJOR "${CMAKE_MATCH_1}" CACHE INTERNAL "" FORCE) + set(Rust_VERSION_MINOR "${CMAKE_MATCH_2}" CACHE INTERNAL "" FORCE) + set(Rust_VERSION_PATCH "${CMAKE_MATCH_3}" CACHE INTERNAL "" FORCE) + set(Rust_VERSION "${Rust_VERSION_MAJOR}.${Rust_VERSION_MINOR}.${Rust_VERSION_PATCH}" CACHE INTERNAL "" FORCE) + if(CMAKE_MATCH_4) + set(Rust_IS_NIGHTLY 1 CACHE INTERNAL "" FORCE) + else() + set(Rust_IS_NIGHTLY 0 CACHE INTERNAL "" FORCE) + endif() +else() + _findrust_failed("Failed to parse rustc version. `${Rust_COMPILER_CACHED} --version --verbose` " + "evaluated to:\n`${_RUSTC_VERSION_RAW}`" + ) +endif() + +if (_RUSTC_VERSION_RAW MATCHES "host: ([a-zA-Z0-9_\\-]*)\n") + set(Rust_DEFAULT_HOST_TARGET "${CMAKE_MATCH_1}") + set(Rust_CARGO_HOST_TARGET_CACHED "${Rust_DEFAULT_HOST_TARGET}" CACHE STRING "Host triple") +else() + _findrust_failed( + "Failed to parse rustc host target. `rustc --version --verbose` evaluated to:\n${_RUSTC_VERSION_RAW}" + ) +endif() + +if (_RUSTC_VERSION_RAW MATCHES "LLVM version: ([0-9]+)\\.([0-9]+)(\\.([0-9]+))?") + set(Rust_LLVM_VERSION_MAJOR "${CMAKE_MATCH_1}" CACHE INTERNAL "" FORCE) + set(Rust_LLVM_VERSION_MINOR "${CMAKE_MATCH_2}" CACHE INTERNAL "" FORCE) + # With the Rust toolchain 1.44.1 the reported LLVM version is 9.0, i.e. without a patch version. + # Since cmake regex does not support non-capturing groups, just ignore Match 3. + set(Rust_LLVM_VERSION_PATCH "${CMAKE_MATCH_4}" CACHE INTERNAL "" FORCE) + set(Rust_LLVM_VERSION "${Rust_LLVM_VERSION_MAJOR}.${Rust_LLVM_VERSION_MINOR}.${Rust_LLVM_VERSION_PATCH}" CACHE INTERNAL "" FORCE) +elseif(NOT Rust_FIND_QUIETLY) + message( + WARNING + "Failed to parse rustc LLVM version. `rustc --version --verbose` evaluated to:\n${_RUSTC_VERSION_RAW}" + ) +endif() + +if (NOT Rust_CARGO_TARGET_CACHED) + unset(_CARGO_ARCH) + unset(_CARGO_ABI) + if (WIN32) + if (CMAKE_VS_PLATFORM_NAME) + string(TOLOWER "${CMAKE_VS_PLATFORM_NAME}" LOWER_VS_PLATFORM_NAME) + if ("${LOWER_VS_PLATFORM_NAME}" STREQUAL "win32") + set(_CARGO_ARCH i686) + elseif("${LOWER_VS_PLATFORM_NAME}" STREQUAL "x64") + set(_CARGO_ARCH x86_64) + elseif("${LOWER_VS_PLATFORM_NAME}" STREQUAL "arm64") + set(_CARGO_ARCH aarch64) + else() + message(WARNING "VS Platform '${CMAKE_VS_PLATFORM_NAME}' not recognized") + endif() + endif() + # Fallback path + if(NOT DEFINED _CARGO_ARCH) + # Possible values for windows when not cross-compiling taken from here: + # https://learn.microsoft.com/en-us/windows/win32/winprog64/wow64-implementation-details + # When cross-compiling the user is expected to supply the value, so we match more variants. + if(CMAKE_SYSTEM_PROCESSOR MATCHES "^(AMD64|amd64|x86_64)$") + set(_CARGO_ARCH x86_64) + elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "^(ARM64|arm64|aarch64)$") + set(_CARGO_ARCH aarch64) + elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "^(X86|x86|i686)$") + set(_CARGO_ARCH i686) + elseif(CMAKE_SYSTEM_PROCESSOR STREQUAL "i586") + set(_CARGO_ARCH i586) + elseif(CMAKE_SYSTEM_PROCESSOR STREQUAL "IA64") + message(FATAL_ERROR "No rust target for Intel Itanium.") + elseif(NOT "${CMAKE_SYSTEM_PROCESSOR}") + message(WARNING "Failed to detect target architecture. Please set `CMAKE_SYSTEM_PROCESSOR`" + " to your target architecture or set `Rust_CARGO_TARGET` to your cargo target triple." + ) + else() + message(WARNING "Failed to detect target architecture. Please set " + "`Rust_CARGO_TARGET` to your cargo target triple." + ) + endif() + endif() + + set(_CARGO_VENDOR "pc-windows") + + # The MSVC Generators will always target the msvc ABI. + # For other generators we check the compiler ID and compiler target (if present) + # If no compiler is set and we are not cross-compiling then we just choose the + # default rust host target. + if(DEFINED MSVC + OR "${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC" + OR "${CMAKE_C_COMPILER_ID}" STREQUAL "MSVC" + OR "${CMAKE_CXX_COMPILER_TARGET}" MATCHES "-msvc$" + OR "${CMAKE_C_COMPILER_TARGET}" MATCHES "-msvc$" + ) + set(_CARGO_ABI msvc) + elseif("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU" + OR "${CMAKE_C_COMPILER_ID}" STREQUAL "GNU" + OR (NOT CMAKE_CROSSCOMPILING + AND NOT DEFINED CMAKE_CXX_COMPILER_ID + AND NOT DEFINED CMAKE_C_COMPILER_ID + AND "${Rust_DEFAULT_HOST_TARGET}" MATCHES "-gnu$" + ) + ) + set(_CARGO_ABI gnu) + elseif(("${CMAKE_C_COMPILER_ID}" MATCHES "Clang$" OR "${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang$") + AND ("${CMAKE_CXX_COMPILER_TARGET}" MATCHES "-gnu(llvm)?$" + OR "${CMAKE_C_COMPILER_TARGET}" MATCHES "-gnu(llvm)?$") + ) + if("${Rust_VERSION}" VERSION_GREATER_EQUAL "1.79") + set(_CARGO_ABI gnullvm) + else() + message(WARNING "Your selected C/C++ compilers suggest you want to use the -gnullvm" + " rust targets, however your Rust compiler version is ${Rust_VERSION}, which is" + " before the promotion of the gnullvm target to tier2." + " Please either use a more recent rust compiler or manually choose a target " + " triple by specifying `Rust_CARGO_TARGET` manually." + ) + endif() + elseif(NOT "${CMAKE_CROSSCOMPILING}" AND "${Rust_DEFAULT_HOST_TARGET}" MATCHES "-msvc$") + # We first check if the gnu branches match to ensure this fallback is only used + # if no compiler is enabled. + set(_CARGO_ABI msvc) + else() + message(WARNING "Could not determine the target ABI. Please specify `Rust_CARGO_TARGET` manually.") + endif() + + if(DEFINED _CARGO_ARCH AND DEFINED _CARGO_VENDOR AND DEFINED _CARGO_ABI) + set(Rust_CARGO_TARGET_CACHED "${_CARGO_ARCH}-${_CARGO_VENDOR}-${_CARGO_ABI}" + CACHE STRING "Target triple") + endif() + elseif (ANDROID) + if (CMAKE_ANDROID_ARCH_ABI STREQUAL armeabi-v7a) + if (CMAKE_ANDROID_ARM_MODE) + set(_Rust_ANDROID_TARGET armv7-linux-androideabi) + else () + set(_Rust_ANDROID_TARGET thumbv7neon-linux-androideabi) + endif() + elseif (CMAKE_ANDROID_ARCH_ABI STREQUAL arm64-v8a) + set(_Rust_ANDROID_TARGET aarch64-linux-android) + elseif (CMAKE_ANDROID_ARCH_ABI STREQUAL x86) + set(_Rust_ANDROID_TARGET i686-linux-android) + elseif (CMAKE_ANDROID_ARCH_ABI STREQUAL x86_64) + set(_Rust_ANDROID_TARGET x86_64-linux-android) + endif() + + if (_Rust_ANDROID_TARGET) + set(Rust_CARGO_TARGET_CACHED "${_Rust_ANDROID_TARGET}" CACHE STRING "Target triple") + endif() + elseif("${CMAKE_SYSTEM_NAME}" STREQUAL "OHOS") + if(CMAKE_OHOS_ARCH_ABI STREQUAL arm64-v8a) + set(_RUST_OHOS_TARGET aarch64-unknown-linux-ohos) + elseif(CMAKE_OHOS_ARCH_ABI STREQUAL armeabi-v7a) + set(_RUST_OHOS_TARGET armv7-unknown-linux-ohos) + elseif(CMAKE_OHOS_ARCH_ABI STREQUAL x86_64) + set(_RUST_OHOS_TARGET x86_64-unknown-linux-ohos) + else() + message(WARNING "unrecognized OHOS architecture: ${OHOS_ARCH}") + endif() + if(_RUST_OHOS_TARGET) + set(Rust_CARGO_TARGET_CACHED "${_RUST_OHOS_TARGET}" CACHE STRING "Target triple") + endif() + endif() + # Fallback to the default host target + if(NOT Rust_CARGO_TARGET_CACHED) + if(CMAKE_CROSSCOMPILING) + message(WARNING "CMake is in cross-compiling mode, but the cargo target-triple could not be inferred." + "Falling back to the default host target. Please consider manually setting `Rust_CARGO_TARGET`." + ) + endif() + set(Rust_CARGO_TARGET_CACHED "${Rust_DEFAULT_HOST_TARGET}" CACHE STRING "Target triple") + endif() + + message(STATUS "Rust Target: ${Rust_CARGO_TARGET_CACHED}") +endif() + + +if(Rust_TOOLCHAIN_IS_RUSTUP_MANAGED) + execute_process(COMMAND rustup target list --toolchain "${Rust_TOOLCHAIN}" + OUTPUT_VARIABLE AVAILABLE_TARGETS_RAW + ) + string(REPLACE "\n" ";" AVAILABLE_TARGETS_RAW "${AVAILABLE_TARGETS_RAW}") + string(REPLACE " (installed)" "" "AVAILABLE_TARGETS" "${AVAILABLE_TARGETS_RAW}") + set(INSTALLED_TARGETS_RAW "${AVAILABLE_TARGETS_RAW}") + list(FILTER INSTALLED_TARGETS_RAW INCLUDE REGEX " \\(installed\\)") + string(REPLACE " (installed)" "" "INSTALLED_TARGETS" "${INSTALLED_TARGETS_RAW}") + list(TRANSFORM INSTALLED_TARGETS STRIP) + if("${Rust_CARGO_TARGET_CACHED}" IN_LIST AVAILABLE_TARGETS) + message(DEBUG "Cargo target ${Rust_CARGO_TARGET} is an official target-triple") + message(DEBUG "Installed targets: ${INSTALLED_TARGETS}") + if(NOT ("${Rust_CARGO_TARGET_CACHED}" IN_LIST INSTALLED_TARGETS)) + if(Rust_RUSTUP_INSTALL_MISSING_TARGET) + message(STATUS "Cargo target ${Rust_CARGO_TARGET_CACHED} is not installed. Installing via rustup.") + execute_process(COMMAND "${Rust_RUSTUP}" target add + --toolchain ${Rust_TOOLCHAIN} + ${Rust_CARGO_TARGET_CACHED} + RESULT_VARIABLE target_add_result + ) + if(NOT "${target_add_result}" EQUAL "0") + message(FATAL_ERROR "Target ${Rust_CARGO_TARGET_CACHED} is not installed for toolchain " + "${Rust_TOOLCHAIN} and automatically installing failed with ${target_add_result}.\n" + "You can try to manually install by running\n" + "`rustup target add --toolchain ${Rust_TOOLCHAIN} ${Rust_CARGO_TARGET}`." + ) + endif() + message(STATUS "Installed target ${Rust_CARGO_TARGET_CACHED} successfully.") + else() + message(FATAL_ERROR "Target ${Rust_CARGO_TARGET_CACHED} is not installed for toolchain ${Rust_TOOLCHAIN}.\n" + "Help: Run `rustup target add --toolchain ${Rust_TOOLCHAIN} ${Rust_CARGO_TARGET_CACHED}` to install " + "the missing target or configure corrosion with `Rust_RUSTUP_INSTALL_MISSING_TARGET=ON`." + ) + endif() + endif() + endif() +endif() + +if(Rust_CARGO_TARGET_CACHED STREQUAL Rust_DEFAULT_HOST_TARGET) + set(Rust_CROSSCOMPILING FALSE CACHE INTERNAL "Rust is configured for cross-compiling") +else() + set(Rust_CROSSCOMPILING TRUE CACHE INTERNAL "Rust is configured for cross-compiling") +endif() + +_corrosion_parse_target_triple("${Rust_CARGO_TARGET_CACHED}" rust_arch rust_vendor rust_os rust_env) +_corrosion_parse_target_triple("${Rust_CARGO_HOST_TARGET_CACHED}" rust_host_arch rust_host_vendor rust_host_os rust_host_env) + +set(Rust_CARGO_TARGET_ARCH "${rust_arch}" CACHE INTERNAL "Target architecture") +set(Rust_CARGO_TARGET_VENDOR "${rust_vendor}" CACHE INTERNAL "Target vendor") +set(Rust_CARGO_TARGET_OS "${rust_os}" CACHE INTERNAL "Target Operating System") +set(Rust_CARGO_TARGET_ENV "${rust_env}" CACHE INTERNAL "Target environment") + +set(Rust_CARGO_HOST_ARCH "${rust_host_arch}" CACHE INTERNAL "Host architecture") +set(Rust_CARGO_HOST_VENDOR "${rust_host_vendor}" CACHE INTERNAL "Host vendor") +set(Rust_CARGO_HOST_OS "${rust_host_os}" CACHE INTERNAL "Host Operating System") +set(Rust_CARGO_HOST_ENV "${rust_host_env}" CACHE INTERNAL "Host environment") + +if(NOT DEFINED CACHE{Rust_CARGO_TARGET_LINK_NATIVE_LIBS}) + message(STATUS "Determining required link libraries for target ${Rust_CARGO_TARGET_CACHED}") + unset(required_native_libs) + _corrosion_determine_libs_new("${Rust_CARGO_TARGET_CACHED}" required_native_libs required_link_flags) + if(DEFINED required_native_libs) + message(STATUS "Required static libs for target ${Rust_CARGO_TARGET_CACHED}: ${required_native_libs}" ) + endif() + if(DEFINED required_link_flags) + message(STATUS "Required link flags for target ${Rust_CARGO_TARGET_CACHED}: ${required_link_flags}" ) + endif() + # In very recent corrosion versions it is possible to override the rust compiler version + # per target, so to be totally correct we would need to determine the libraries for + # every installed Rust version, that the user could choose from. + # In practice there aren't likely going to be any major differences, so we just do it once + # for the target and once for the host target (if cross-compiling). + set(Rust_CARGO_TARGET_LINK_NATIVE_LIBS "${required_native_libs}" CACHE INTERNAL + "Required native libraries when linking Rust static libraries") + set(Rust_CARGO_TARGET_LINK_OPTIONS "${required_link_flags}" CACHE INTERNAL + "Required link flags when linking Rust static libraries") +endif() + +if(Rust_CROSSCOMPILING AND NOT DEFINED CACHE{Rust_CARGO_HOST_TARGET_LINK_NATIVE_LIBS}) + message(STATUS "Determining required link libraries for target ${Rust_CARGO_HOST_TARGET_CACHED}") + unset(host_libs) + _corrosion_determine_libs_new("${Rust_CARGO_HOST_TARGET_CACHED}" host_libs host_flags) + if(DEFINED host_libs) + message(STATUS "Required static libs for host target ${Rust_CARGO_HOST_TARGET_CACHED}: ${host_libs}" ) + endif() + set(Rust_CARGO_HOST_TARGET_LINK_NATIVE_LIBS "${host_libs}" CACHE INTERNAL + "Required native libraries when linking Rust static libraries for the host target") + set(Rust_CARGO_HOST_TARGET_LINK_OPTIONS "${host_flags}" CACHE INTERNAL + "Required linker flags when linking Rust static libraries for the host target") +endif() + +# Set the input variables as non-cache variables so that the variables are available after +# `find_package`, even if the values were evaluated to defaults. +foreach(_VAR ${_Rust_USER_VARS}) + set(${_VAR} "${${_VAR}_CACHED}") + # Ensure cached variables have type INTERNAL + set(${_VAR}_CACHED "${${_VAR}_CACHED}" CACHE INTERNAL "Internal cache of ${_VAR}") +endforeach() + +find_package_handle_standard_args( + Rust + REQUIRED_VARS Rust_COMPILER Rust_VERSION Rust_CARGO Rust_CARGO_VERSION Rust_CARGO_TARGET Rust_CARGO_HOST_TARGET + VERSION_VAR Rust_VERSION +) + + +if(NOT TARGET Rust::Rustc) + add_executable(Rust::Rustc IMPORTED GLOBAL) + set_property( + TARGET Rust::Rustc + PROPERTY IMPORTED_LOCATION "${Rust_COMPILER_CACHED}" + ) + + add_executable(Rust::Cargo IMPORTED GLOBAL) + set_property( + TARGET Rust::Cargo + PROPERTY IMPORTED_LOCATION "${Rust_CARGO_CACHED}" + ) + set(Rust_FOUND true) +endif() + +list(POP_BACK CMAKE_MESSAGE_CONTEXT) diff -r 2b4f361e3891 -r 629d5123a979 tools/corrosion/doc/.gitignore --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/corrosion/doc/.gitignore Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,1 @@ +book diff -r 2b4f361e3891 -r 629d5123a979 tools/corrosion/doc/book.toml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/corrosion/doc/book.toml Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,5 @@ +[book] +language = "en" +multilingual = false +src = "src" +title = "Corrosion documentation" diff -r 2b4f361e3891 -r 629d5123a979 tools/corrosion/doc/src/SUMMARY.md --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/corrosion/doc/src/SUMMARY.md Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,9 @@ +# Summary + +- [Introduction](./introduction.md) +- [Quick Start](./quick_start.md) +- [Setup Corrosion](./setup_corrosion.md) +- [Usage](./usage.md) +- [Advanced](./advanced.md) +- [FFI binding integrations](./ffi_bindings.md) +- [Common Issues](./common_issues.md) diff -r 2b4f361e3891 -r 629d5123a979 tools/corrosion/doc/src/advanced.md --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/corrosion/doc/src/advanced.md Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,100 @@ +## What does corrosion do? + +The specifics of what corrosion does should be regarded as an implementation detail and not relied on +when writing user code. However, a basic understanding of what corrosion does may be helpful when investigating +issues. + +### FindRust + +Corrosion maintains a CMake module `FindRust` which is executed when Corrosion is loaded, i.e. at the time +of `find_package(corrosion)`, `FetchContent_MakeAvailable(corrosion)` or `add_subdirectory(corrosion)` depending +on the method used to include Corrosion. + +`FindRust` will search for installed rust toolchains, respecting the options prefixed with `Rust_` documented in +the [Usage](usage.md#corrosion-options) chapter. +It will select _one_ Rust toolchain to be used for the compilation of Rust code. Toolchains managed by `rustup` +will be resolved and corrosion will always select a specific toolchain, not a `rustup` proxy. + + +### Importing Rust crates + +Corrosion's main function is `corrosion_import_crate`, which internally will call `cargo metadata` to provide +structured information based on the `Cargo.toml` manifest. +Corrosion will then iterate over all workspace and/or package members and find all rust crates that are either +a static (`staticlib`) or shared (`cdylib`) library or a `bin` target and create CMake targets matching the +crate name. Additionally, a build target is created for each imported target, containing the required build +command to create the imported artifact. This build command can be influenced by various arguments to +`corrosion_import_crate` as well as corrosion specific target properties which are documented int the +[Usage](usage.md) chapter. +Corrosion adds the necessary dependencies and also copies the target artifacts out of the cargo build tree +to standard CMake locations, even respecting `OUTPUT_DIRECTORY` target properties if set. + +### Linking + +Depending on the type of the crate the linker will either be invoked by CMake or by `rustc`. +Rust `staticlib`s are linked into C/C++ code via `target_link_libraries()` and the linker is +invoked by CMake. +For rust `cdylib`s and `bin`s, the linker is invoked via `rustc` and CMake just gets the final artifact. + +#### CMake invokes the linker + +When CMake invokes the linker, everything is as usual. CMake will call the linker with +the compiler as the linker driver and users can just use the regular CMake functions to +modify linking behaviour. `corrosion_set_linker()` has **no effect**. +As a convenience, `corrosion_link_libraries()` will forward its arguments to `target_link_libraries()`. + +#### Rustc invokes the linker + +Rust `cdylib`s and `bin`s are linked via `rustc`. Corrosion provides several helper functions +to influence the linker invocation for such targets. + +`corrosion_link_libraries()` is a limited version of `target_link_libraries()` +for rust `cdylib` or `bin` targets. +Under the hood this function passes `-l` and `-L` flags to the linker invocation and +ensures the linked libraries are built first. +Much of the advanced functionality available in `target_link_libraries()` is not implemented yet, +but pull-requests are welcome! In the meantime, users may want to use +`corrosion_add_target_local_rustflags()` to pass customized linking flags. + +`corrosion_set_linker()` can be used to specify a custom linker, in case the default one +chosen by corrosion is not what you want. +Corrosion currently instructs `rustc` to use the C/C++ compiler as the linker driver. +This is done because: +- For C++ code we must link with `libstdc++` or `libc++` (depending on the compiler), so we must + either specify the library on the link line or use a `c++` compiler as the linker driver. +- `Rustc`s default linker selection currently is not so great. For a number of platforms + `rustc` will fallback to `cc` as the linker driver. When cross-compiling, this leads + to linking failures, since the linker driver is for the host architecture. + Corrosion avoids this by specifying the C/C++ compiler as the linker driver. + + +In some cases, especially in older rust versions (pre 1.68), the linker flavor detection +of `rustc` is also not correct, so when setting a custom linker you may want to pass the +[`-C linker-flavor`](https://doc.rust-lang.org/rustc/codegen-options/index.html#linker-flavor) +rustflag via `corrosion_add_target_local_rustflags()`. + +## FFI bindings + +For interaction between Rust and other languages there need to be some FFI bindings of some sort. +For simple cases manually defining the interfaces may be sufficient, but in many cases users +wish to use tools like [bindgen], [cbindgen], [cxx] or [autocxx] to automate the generating of +bindings. + +In principle there are two different ways to generate the bindings: +- use a `build.rs` script to generate the bindings when cargo is invoked, using + library versions of the tools to generate the bindings. +- use the cli versions of the tools and setup custom CMake targets/commands to + generate the bindings. This approach should be preferred if the bindings are needed + by the C/C++ side. + +Corrosion currently provides 2 experimental functions to integrate cbindgen and cxx into +the build process. They are not 100% production ready yet, but should work well as a +template on how to integrate generating bindings into your build process. + +Todo: expand this documentation and link to other resources. + +[bindgen]: https://rust-lang.github.io/rust-bindgen/ +[cbindgen]: https://github.com/eqrion/cbindgen +[cxx]: https://cxx.rs/ +[autocxx]: https://google.github.io/autocxx/index.html + \ No newline at end of file diff -r 2b4f361e3891 -r 629d5123a979 tools/corrosion/doc/src/common_issues.md --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/corrosion/doc/src/common_issues.md Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,88 @@ +# Commonly encountered (Non-Corrosion) Issues + +## Table of Contents + +- [Linking Debug C/C++ libraries into Rust fails on Windows MSVC targets](#linking-debug-cc-libraries-into-rust-fails-on-windows-msvc-targets) +- [Linking Rust static libraries into Debug C/C++ binaries fails on Windows MSVC targets](#linking-rust-static-libraries-into-debug-cc-binaries-fails-on-windows-msvc-targets) +- [Missing `soname` on Linux for `cdylibs`](#missing-soname-on-linux-for-cdylibs) +- [Missing `install_name` on MacOS for `ccdylibs` / Hardcoded references to the build-directory](#missing-installname-on-macos-for-ccdylibs--hardcoded-references-to-the-build-directory) + +## Linking Debug C/C++ libraries into Rust fails on Windows MSVC targets + +`rustc` always links against the non-debug Windows runtime on `*-msvc` targets. +This is tracked [in this issue](https://github.com/rust-lang/rust/issues/39016) +and could be fixed upstream. + +A typical error message for this issue is: + +``` + Compiling rust_bin v0.1.0 (D:\a\corrosion\corrosion\test\cxxbridge\cxxbridge_cpp2rust\rust) +error: linking with `link.exe` failed: exit code: 1319 +[ redacted ] + = note: cxxbridge-cpp.lib(lib.cpp.obj) : error LNK2038: mismatch detected for '_ITERATOR_DEBUG_LEVEL': value '2' doesn't match value '0' in libcxx-bafec361a1a30317.rlib(cxx.o) + + cxxbridge-cpp.lib(lib.cpp.obj) : error LNK2038: mismatch detected for 'RuntimeLibrary': value 'MDd_DynamicDebug' doesn't match value 'MD_DynamicRelease' in libcxx-bafec361a1a30317.rlib(cxx.o) + + cpp_lib.lib(cpplib.cpp.obj) : error LNK2038: mismatch detected for '_ITERATOR_DEBUG_LEVEL': value '2' doesn't match value '0' in libcxx-bafec361a1a30317.rlib(cxx.o) + + cpp_lib.lib(cpplib.cpp.obj) : error LNK2038: mismatch detected for 'RuntimeLibrary': value 'MDd_DynamicDebug' doesn't match value 'MD_DynamicRelease' in libcxx-bafec361a1a30317.rlib(cxx.o) + + msvcrt.lib(initializers.obj) : warning LNK4098: defaultlib 'msvcrtd.lib' conflicts with use of other libs; use /NODEFAULTLIB:library +``` + +### Solutions + +One solution is to also use the non-debug version when building the C/C++ libraries. +You can set the [MSVC_RUNTIME_LIBRARY] target properties of your C/C++ libraries to the non-debug variants. +By default you will probably want to select the `MultiThreadedDLL` variant, unless you specified +[`-Ctarget-feature=+crt-static`](https://rust-lang.github.io/rfcs/1721-crt-static.html) in your +`RUSTFLAGS`. + + +[MSVC_RUNTIME_LIBRARY]: https://cmake.org/cmake/help/latest/prop_tgt/MSVC_RUNTIME_LIBRARY.html#prop_tgt:MSVC_RUNTIME_LIBRARY + +## Linking Rust static libraries into Debug C/C++ binaries fails on Windows MSVC targets + +This issue is quite similar to the previous one, except that this time it's a Rust library being linked +into a C/C++ target. If it's 100% only Rust code you likely won't even have any issues. +However, if somewhere in the dependency graph C/C++ code is built and linked into your Rust library, +you will likely encounter this issue. Please note, that using [cxx] counts as using C++ code and will +lead to this issue. + +The previous solution should also work for this case, but additionally you [may also +have success](https://github.com/rust-lang/rust/issues/39016#issuecomment-853964918) by using +`corrosion_set_env_vars(your_rust_lib "CFLAGS=-MDd" "CXXFLAGS=-MDd")` (or `-MTd` for a statically linked +runtime). +For debug builds, this is likely to be the preferable solution. It assumes that downstream C/C++ code +is built by the `cc` crate, which respects the `CFLAGS` and `CXXFLAGS` environment variables. + +[cxx]: https://github.com/dtolnay/cxx + + +## Missing `soname` on Linux for `cdylibs` + +Cargo doesn't support setting the `soname` field for cdylib, which may cause issues. +You can set the soname manually by passing a linker-flag such as `-Clink-arg=-Wl,-soname,libyour_crate.so` +to the linker via `corrosion_add_target_local_rustflags()` and additionally seting the `IMPORTED_SONAME` +property on the import CMake target: +``` +set_target_properties(your_crate-shared PROPERTIES IMPORTED_SONAME libyour_crate.so) +``` +Replace `your_crate` with the name of your shared library as defined in the `[lib]` section of your Cargo.toml +Manifest file. + +Attention: The Linux section may not be entirely correct, maybe `$ORIGIN` needs to be added to the linker arguments. +Feel free to open a pull-request with corrections. + +## Missing `install_name` on MacOS for `ccdylibs` / Hardcoded references to the build-directory + +The solution here is essentially the same as in the previous section. +``` +corrosion_add_target_local_rustflags(your_crate -Clink-arg=-Wl,-install_name,@rpath/libyour_crate.dylib,-current_version,${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR},-compatibility_version,${PROJECT_VERSION_MAJOR}.0) +set_target_properties(your_crate-shared PROPERTIES IMPORTED_NO_SONAME 0) +set_target_properties(your_crate-shared PROPERTIES IMPORTED_SONAME libyour_crate.dylib) +``` +When building binaries using this shared library, you should set the build rpath to the output directory of +your shared library, e.g. by setting `set(CMAKE_BUILD_RPATH ${YOUR_CUSTOM_OUTPUT_DIRECTORY})` before adding +executables. +For a practical example, you may look at [Slint PR 2455](https://github.com/slint-ui/slint/pull/2455). diff -r 2b4f361e3891 -r 629d5123a979 tools/corrosion/doc/src/ffi_bindings.md --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/corrosion/doc/src/ffi_bindings.md Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,43 @@ +# Integrating Automatically Generated FFI Bindings + +There are a number of tools to automatically generate bindings between Rust and different +foreign languages. + +1. [bindgen](#bindgen) +2. [cbindgen](#cbindgen-integration) +3. [cxx](#cxx-integration) + +## bindgen + +[bindgen] is a tool to automatically generate Rust bindings from C headers. +As such, integrating bindgen [via a build-script](https://rust-lang.github.io/rust-bindgen/library-usage.html) +works well and their doesn't seem to be a need to create CMake rules for +generating the bindings. + +[bindgen]: https://github.com/rust-lang/rust-bindgen + +## cbindgen integration + +⚠️⚠️⚠️ **EXPERIMENTAL** ⚠️⚠️⚠️ + +[cbindgen] is a tool that generates C/C++ headers from Rust code. When compiling C/C++ +code that `#include`s such generated headers the buildsystem must be aware of the dependencies. +Generating the headers via a build-script is possible, but Corrosion offers no guidance here. + +Instead, Corrosion offers an experimental function to add CMake rules using cbindgen to generate +the headers. +This is not available on a stable released version yet, and the details are subject to change. +{{#include ../../cmake/Corrosion.cmake:corrosion_cbindgen}} + +### Current limitations + +- The current version regenerates the bindings more often then necessary to be on the safe side, + but an upstream PR is open to solve this in a future cbindgen version. + +## cxx integration + +⚠️⚠️⚠️ **EXPERIMENTAL** ⚠️⚠️⚠️ + +[cxx] is a tool which generates bindings for C++/Rust interop. + +{{#include ../../cmake/Corrosion.cmake:corrosion_add_cxxbridge}} diff -r 2b4f361e3891 -r 629d5123a979 tools/corrosion/doc/src/introduction.md --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/corrosion/doc/src/introduction.md Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,19 @@ +## About Corrosion + +Corrosion, formerly known as cmake-cargo, is a tool for integrating Rust into an existing CMake +project. Corrosion is capable of automatically importing executables, static libraries, and +dynamic libraries from a Rust package or workspace as CMake targets. + +The imported static and dynamic library types can be linked into C/C++ CMake targets using the usual +CMake functions such as [`target_link_libraries()`]. +For rust executables and dynamic libraries corrosion provides a `corrosion_link_libraries` +helper function to conveniently add the necessary flags to link C/C++ libraries into +the rust target. + +## Requirements + +- Corrosion supports CMake 3.15 and newer with the v0.5 release. If you are using the v0.5 release, please + view [the documentation here](./v0.5/introduction.md) +- The master branch of Corrosion currently requires CMake 3.22 or newer. + +[`target_link_libraries()`]: https://cmake.org/cmake/help/latest/command/target_link_libraries.html diff -r 2b4f361e3891 -r 629d5123a979 tools/corrosion/doc/src/quick_start.md --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/corrosion/doc/src/quick_start.md Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,37 @@ +# Quick Start + +You can add corrosion to your project via the `FetchContent` CMake module or one of the other methods +described in the [Setup chapter](setup_corrosion.md). +Afterwards you can import Rust targets defined in a `Cargo.toml` manifest file by using +`corrosion_import_crate`. This will add CMake targets with names matching the crate names defined +in the Cargo.toml manifest. These targets can then subsequently be used, e.g. to link the imported +target into a regular C/C++ target. + +The example below shows how to add Corrosion to your project via `FetchContent` +and how to import a rust library and link it into a regular C/C++ CMake target. + +```cmake +include(FetchContent) + +FetchContent_Declare( + Corrosion + GIT_REPOSITORY https://github.com/corrosion-rs/corrosion.git + GIT_TAG v0.5 # Optionally specify a commit hash, version tag or branch here +) +# Set any global configuration variables such as `Rust_TOOLCHAIN` before this line! +FetchContent_MakeAvailable(Corrosion) + +# Import targets defined in a package or workspace manifest `Cargo.toml` file +corrosion_import_crate(MANIFEST_PATH rust-lib/Cargo.toml) + +add_executable(your_cool_cpp_bin main.cpp) + +# In this example the the `Cargo.toml` file passed to `corrosion_import_crate` is assumed to have +# defined a static (`staticlib`) or shared (`cdylib`) rust library with the name "rust-lib". +# A target with the same name is now available in CMake and you can use it to link the rust library into +# your C/C++ CMake target(s). +target_link_libraries(your_cool_cpp_bin PUBLIC rust-lib) +``` + + +Please see the [Usage chapter](usage.md) for a complete discussion of possible configuration options. diff -r 2b4f361e3891 -r 629d5123a979 tools/corrosion/doc/src/setup_corrosion.md --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/corrosion/doc/src/setup_corrosion.md Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,89 @@ +# Adding Corrosion to your project + +There are two fundamental installation methods that are supported by Corrosion - installation as a +CMake package or using it as a subdirectory in an existing CMake project. For CMake versions below +3.19 Corrosion strongly recommends installing the package, either via a package manager or manually +using CMake's installation facilities. +If you have CMake 3.19 or newer, we recommend to use either the [FetchContent](#fetchcontent) or the +[Subdirectory](#subdirectory) method to integrate Corrosion. + +## FetchContent +If you are using CMake >= 3.19 or installation is difficult or not feasible in +your environment, you can use the +[FetchContent](https://cmake.org/cmake/help/latest/module/FetchContent.html) module to include +Corrosion. This will download Corrosion and use it as if it were a subdirectory at configure time. + +In your CMakeLists.txt: +```cmake +include(FetchContent) + +FetchContent_Declare( + Corrosion + GIT_REPOSITORY https://github.com/corrosion-rs/corrosion.git + GIT_TAG v0.5 # Optionally specify a commit hash, version tag or branch here +) +# Set any global configuration variables such as `Rust_TOOLCHAIN` before this line! +FetchContent_MakeAvailable(Corrosion) +``` + +## Subdirectory +Corrosion can also be used directly as a subdirectory. This solution may work well for small +projects, but it's discouraged for large projects with many dependencies, especially those which may +themselves use Corrosion. Either copy the Corrosion library into your source tree, being sure to +preserve the `LICENSE` file, or add this repository as a git submodule: +```bash +git submodule add https://github.com/corrosion-rs/corrosion.git +``` + +From there, using Corrosion is easy. In your CMakeLists.txt: +```cmake +add_subdirectory(path/to/corrosion) +``` + +## Installation + + +Installation will pre-build all of Corrosion's native tooling (required only for CMake versions +below 3.19) and install it together with Corrosions CMake files into a standard location. +On CMake >= 3.19 installing Corrosion does not offer any speed advantages, unless the native +tooling option is explicitly enabled. + +### Install from source + +First, download and install Corrosion: +```bash +git clone https://github.com/corrosion-rs/corrosion.git +# Optionally, specify -DCMAKE_INSTALL_PREFIX= to specify a +# custom installation directory +cmake -Scorrosion -Bbuild -DCMAKE_BUILD_TYPE=Release +cmake --build build --config Release +# This next step may require sudo or admin privileges if you're installing to a system location, +# which is the default. +cmake --install build --config Release +``` + +You'll want to ensure that the install directory is available in your `PATH` or `CMAKE_PREFIX_PATH` +environment variable. This is likely to already be the case by default on a Unix system, but on +Windows it will install to `C:\Program Files (x86)\Corrosion` by default, which will not be in your +`PATH` or `CMAKE_PREFIX_PATH` by default. + +Once Corrosion is installed, and you've ensured the package is available in your `PATH`, you +can use it from your own project like any other package from your CMakeLists.txt: +```cmake +find_package(Corrosion REQUIRED) +``` + +### Package Manager + +#### Homebrew (unofficial) + +Corrosion is available via Homebrew and can be installed via + +```bash +brew install corrosion +``` + +Please note that this package is community maintained. Please also keep in mind that Corrosion follows +semantic versioning and minor version bumps (i.e. `0.3` -> `0.4`) may contain breaking changes, while +Corrosion is still pre `1.0`. +Please read the release notes when upgrading Corrosion. diff -r 2b4f361e3891 -r 629d5123a979 tools/corrosion/doc/src/usage.md --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/corrosion/doc/src/usage.md Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,405 @@ +## Usage + +### Automatically import crate targets with `corrosion_import_crate` + +In order to integrate a Rust crate into CMake, you first need to import Rust crates from +a [package] or [workspace]. Corrosion provides `corrosion_import_crate()` to automatically import +crates defined in a Cargo.toml Manifest file: + +{{#include ../../cmake/Corrosion.cmake:corrosion-import-crate}} + +Corrosion will use `cargo metadata` to add a cmake target for each crate defined in the Manifest file +and add the necessary rules to build the targets. +For Rust executables an [`IMPORTED`] executable target is created with the same name as defined in the `[[bin]]` +section of the Manifest corresponding to this target. +If no such name was defined the target name defaults to the Rust package name. +For Rust library targets an [`INTERFACE`] library target is created with the same name as defined in the `[lib]` +section of the Manifest. This `INTERFACE` library links an internal corrosion target, which is either a +`SHARED` or `STATIC` `IMPORTED` library, depending on the Rust crate type (`cdylib` vs `staticlib`). + +The created library targets can be linked into other CMake targets by simply using [target_link_libraries]. + +Corrosion will by default copy the produced Rust artifacts into `${CMAKE_CURRENT_BINARY_DIR}`. The target location +can be changed by setting the CMake `OUTPUT_DIRECTORY` target properties on the imported Rust targets. +See the [OUTPUT_DIRECTORY](#cmake-output_directory-target-properties-and-imported_location) section for more details. + +Many of the options available for `corrosion_import_crate` can also be individually set per +target, see [Per Target options](#per-target-options) for details. + +[package]: https://doc.rust-lang.org/book/ch07-01-packages-and-crates.html +[workspace]: https://doc.rust-lang.org/cargo/reference/workspaces.html +[`IMPORTED`]: https://cmake.org/cmake/help/latest/prop_tgt/IMPORTED.html +[`INTERFACE`]: https://cmake.org/cmake/help/latest/command/add_library.html#interface-libraries +[target_link_libraries]: https://cmake.org/cmake/help/latest/command/target_link_libraries.html + +### Experimental: Install crate and headers with `corrosion_install` + +The default CMake [install commands] do not work correctly with the targets exported from `corrosion_import_crate()`. +Corrosion provides `corrosion_install` to automatically install relevant files: + +{{#include ../../cmake/Corrosion.cmake:corrosion-install}} + +The example below shows how to import a rust library and make it available for install through CMake. + +```cmake +include(FetchContent) + +FetchContent_Declare( + Corrosion + GIT_REPOSITORY https://github.com/corrosion-rs/corrosion.git + GIT_TAG v0.5 # Optionally specify a commit hash, version tag or branch here +) +# Set any global configuration variables such as `Rust_TOOLCHAIN` before this line! +FetchContent_MakeAvailable(Corrosion) + +# Import targets defined in a package or workspace manifest `Cargo.toml` file +corrosion_import_crate(MANIFEST_PATH rust-lib/Cargo.toml) + +# Add a manually written header file which will be exported +# Requires CMake >=3.23 +target_sources(rust-lib INTERFACE + FILE_SET HEADERS + BASE_DIRS include + FILES + include/rust-lib/rust-lib.h +) + +# OR for CMake <= 3.23 +target_include_directories(is_odd INTERFACE + $ + $ +) +target_sources(is_odd + INTERFACE + $ + $ +) + +# Rust libraries must be installed using `corrosion_install`. +corrosion_install(TARGETS rust-lib EXPORT RustLibTargets) + +# Installs the main target +install( + EXPORT RustLibTargets + NAMESPACE RustLib:: + DESTINATION lib/cmake/RustLib +) + +# Necessary for packaging helper commands +include(CMakePackageConfigHelpers) +# Create a file for checking version compatibility +# Optional +write_basic_package_version_file( + "${CMAKE_CURRENT_BINARY_DIR}/RustLibConfigVersion.cmake" + VERSION "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}" + COMPATIBILITY AnyNewerVersion +) + +# Configures the main config file that cmake loads +configure_package_config_file(${CMAKE_CURRENT_SOURCE_DIR}/Config.cmake.in + "${CMAKE_CURRENT_BINARY_DIR}/RustLibConfig.cmake" + INSTALL_DESTINATION lib/cmake/RustLib + NO_SET_AND_CHECK_MACRO + NO_CHECK_REQUIRED_COMPONENTS_MACRO +) +# Config.cmake.in contains +# @PACKAGE_INIT@ +# +# include(${CMAKE_CURRENT_LIST_DIR}/RustLibTargetsCorrosion.cmake) +# include(${CMAKE_CURRENT_LIST_DIR}/RustLibTargets.cmake) + +# Install all generated files +install(FILES + ${CMAKE_CURRENT_BINARY_DIR}/RustLibConfigVersion.cmake + ${CMAKE_CURRENT_BINARY_DIR}/RustLibConfig.cmake + ${CMAKE_CURRENT_BINARY_DIR}/corrosion/RustLibTargetsCorrosion.cmake + DESTINATION lib/cmake/RustLib +) +``` + +[install commands]: https://cmake.org/cmake/help/latest/command/install.html + +### Per Target options + +Some configuration options can be specified individually for each target. You can set them via the +`corrosion_set_xxx()` functions specified below: + +- `corrosion_set_env_vars( [... ])`: Define environment variables + that should be set during the invocation of `cargo build` for the specified target. Please note that + the environment variable will only be set for direct builds of the target via cmake, and not for any + build where cargo built the crate in question as a dependency for another target. + The environment variables may contain generator expressions. +- `corrosion_add_target_rustflags( [... ])`: When building the target, + the `RUSTFLAGS` environment variable will contain the flags added via this function. Please note that any + dependencies (built by cargo) will also see these flags. See also: `corrosion_add_target_local_rustflags`. +- `corrosion_add_target_local_rustflags(target_name rustc_flag [more_flags ...])`: Support setting + rustflags for only the main target (crate) and none of its dependencies. + This is useful in cases where you only need rustflags on the main-crate, but need to set different + flags for different targets. Without "local" Rustflags this would require rebuilds of the + dependencies when switching targets. +- `corrosion_set_hostbuild()`: The target should be compiled for the Host target and ignore any + cross-compile configuration. +- `corrosion_set_features( [ALL_FEATURES ] [NO_DEFAULT_FEATURES] [FEATURES ... ])`: + For a given target, enable specific features via `FEATURES`, toggle `ALL_FEATURES` on or off or disable all features + via `NO_DEFAULT_FEATURES`. For more information on features, please see also the + [cargo reference](https://doc.rust-lang.org/cargo/reference/features.html). +- `corrosion_set_cargo_flags( ...])`: + For a given target, add options and flags at the end of `cargo build` invocation. This will be appended after any + arguments passed through the `FLAGS` during the crate import. +- `corrosion_set_linker(target_name linker)`: Use `linker` to link the target. + Please note that this only has an effect for targets where the final linker invocation is done + by cargo, i.e. targets where foreign code is linked into rust code and not the other way around. + Please also note that if you are cross-compiling and specify a linker such as `clang`, you are + responsible for also adding a rustflag which adds the necessary `--target=` argument for the + linker. + + +### Global Corrosion Options + +#### Selecting the Rust toolchain and target triple + +The following variables are evaluated automatically in most cases. In typical cases you +shouldn't need to alter any of these. If you do want to specify them manually, make sure to set +them **before** `find_package(Corrosion REQUIRED)`. + +- `Rust_TOOLCHAIN:STRING` - Specify a named rustup toolchain to use. Changes to this variable + resets all other options. Default: If the first-found `rustc` is a `rustup` proxy, then the default + rustup toolchain (see `rustup show`) is used. Otherwise, the variable is unset by default. +- `Rust_ROOT:STRING` - CMake provided. Path to a Rust toolchain to use. This is an alternative if + you want to select a specific Rust toolchain, but it's not managed by rustup. Default: Nothing +- `Rust_COMPILER:STRING` - Path to `rustc`, which should be used for compiling or for toolchain + detection (if it is a `rustup` proxy). Default: The `rustc` in the first-found toolchain, either + from `rustup`, or from a toolchain available in the user's `PATH`. +- `Rust_RESOLVE_RUSTUP_TOOLCHAINS:BOOL` - If the found `rustc` is a `rustup` proxy, resolve a + concrete path to a specific toolchain managed by `rustup`, according to the `rustup` toolchain + selection rules and other options detailed here. If this option is turned off, the found `rustc` + will be used as-is to compile, even if it is a `rustup` proxy, which might increase compilation + time. Default: `ON` if the found `rustc` is a rustup proxy or a `rustup` managed toolchain was + requested, `OFF` otherwise. Forced `OFF` if `rustup` was not found. +- `Rust_CARGO:STRING` - Path to `cargo`. Default: the `cargo` installed next to `${Rust_COMPILER}`. +- `Rust_CARGO_TARGET:STRING` - The default target triple to build for. Alter for cross-compiling. + Default: On Visual Studio Generator, the matching triple for `CMAKE_VS_PLATFORM_NAME`. Otherwise, + the default target triple reported by `${Rust_COMPILER} --version --verbose`. + +#### Enable Convenience Options + +The following options are off by default, but may increase convenience: + +- `Rust_RUSTUP_INSTALL_MISSING_TARGET:BOOL`: Automatically install a missing target via `rustup` instead of failing. + + +#### Developer/Maintainer Options +These options are not used in the course of normal Corrosion usage, but are used to configure how +Corrosion is built and installed. Only applies to Corrosion builds and subdirectory uses. + +- `CORROSION_BUILD_TESTS:BOOL` - Build the Corrosion tests. Default: `Off` if Corrosion is a + subdirectory, `ON` if it is the top-level project + + +### Information provided by Corrosion + +For your convenience, Corrosion sets a number of variables which contain information about the version of the rust +toolchain. You can use the CMake version comparison operators +(e.g. [`VERSION_GREATER_EQUAL`](https://cmake.org/cmake/help/latest/command/if.html#version-comparisons)) on the main +variable (e.g. `if(Rust_VERSION VERSION_GREATER_EQUAL "1.57.0")`), or you can inspect the major, minor and patch +versions individually. +- `Rust_VERSION<_MAJOR|_MINOR|_PATCH>` - The version of rustc. +- `Rust_CARGO_VERSION<_MAJOR|_MINOR|_PATCH>` - The cargo version. +- `Rust_LLVM_VERSION<_MAJOR|_MINOR|_PATCH>` - The LLVM version used by rustc. +- `Rust_IS_NIGHTLY` - 1 if a nightly toolchain is used, otherwise 0. Useful for selecting an unstable feature for a + crate, that is only available on nightly toolchains. +- Cache variables containing information based on the target triple for the selected target + as well as the default host target: + - `Rust_CARGO_TARGET_ARCH`, `Rust_CARGO_HOST_ARCH`: e.g. `x86_64` or `aarch64` + - `Rust_CARGO_TARGET_VENDOR`, `Rust_CARGO_HOST_VENDOR`: e.g. `apple`, `pc`, `unknown` etc. + - `Rust_CARGO_TARGET_OS`, `Rust_CARGO_HOST_OS`: e.g. `darwin`, `linux`, `windows`, `none` + - `Rust_CARGO_TARGET_ENV`, `Rust_CARGO_HOST_ENV`: e.g. `gnu`, `musl` + + + + +### Selecting a custom cargo profile + +[Rust 1.57](https://blog.rust-lang.org/2021/12/02/Rust-1.57.0.html) stabilized the support for custom +[profiles](https://doc.rust-lang.org/cargo/reference/profiles.html). If you are using a sufficiently new rust toolchain, +you may select a custom profile by adding the optional argument `PROFILE ` to +`corrosion_import_crate()`. If you do not specify a profile, or you use an older toolchain, corrosion will select +the standard `dev` profile if the CMake config is either `Debug` or unspecified. In all other cases the `release` +profile is chosen for cargo. + +### Importing C-Style Libraries Written in Rust +Corrosion makes it completely trivial to import a crate into an existing CMake project. Consider +a project called [rust2cpp](test/rust2cpp/rust2cpp) with the following file structure: +``` +rust2cpp/ + rust/ + src/ + lib.rs + Cargo.lock + Cargo.toml + CMakeLists.txt + main.cpp +``` + +This project defines a simple Rust lib crate, like so, in [`rust2cpp/rust/Cargo.toml`](test/rust2cpp/rust2cpp/rust/Cargo.toml): +```toml +[package] +name = "rust-lib" +version = "0.1.0" +authors = ["Andrew Gaspar "] +license = "MIT" +edition = "2018" + +[dependencies] + +[lib] +crate-type=["staticlib"] +``` + +In addition to `"staticlib"`, you can also use `"cdylib"`. In fact, you can define both with a +single crate and switch between which is used using the standard +[`BUILD_SHARED_LIBS`](https://cmake.org/cmake/help/latest/variable/BUILD_SHARED_LIBS.html) variable. + +This crate defines a simple crate called `rust-lib`. Importing this crate into your +[CMakeLists.txt](test/rust2cpp/CMakeLists.txt) is trivial: +```cmake +# Note: you must have already included Corrosion for `corrosion_import_crate` to be available. See # the `Installation` section above. + +corrosion_import_crate(MANIFEST_PATH rust/Cargo.toml) +``` + +Now that you've imported the crate into CMake, all of the executables, static libraries, and dynamic +libraries defined in the Rust can be directly referenced. So, merely define your C++ executable as +normal in CMake and add your crate's library using target_link_libraries: +```cmake +add_executable(cpp-exe main.cpp) +target_link_libraries(cpp-exe PUBLIC rust-lib) +``` + +That's it! You're now linking your Rust library to your C++ library. + +#### Generate Bindings to Rust Library Automatically + +Currently, you must manually declare bindings in your C or C++ program to the exported routines and +types in your Rust project. You can see boths sides of this in +[the Rust code](test/rust2cpp/rust2cpp/rust/src/lib.rs) and in [the C++ code](test/rust2cpp/rust2cpp/main.cpp). + +Integration with [cbindgen](https://github.com/eqrion/cbindgen) is +planned for the future. + +### Importing Libraries Written in C and C++ Into Rust + +The rust targets can be imported with `corrosion_import_crate()` into CMake. +For targets where the linker should be invoked by Rust corrosion provides +`corrosion_link_libraries()` to link your C/C++ libraries with the Rust target. +For additional linker flags you may use `corrosion_add_target_local_rustflags()` +and pass linker arguments via the `-Clink-args` flag to rustc. These flags will +only be passed to the final rustc invocation and not affect any rust dependencies. + +C bindings can be generated via [bindgen](https://github.com/rust-lang/rust-bindgen). +Corrosion does not offer any direct integration yet, but you can either generate the +bindings in the build-script of your crate, or generate the bindings as a CMake build step +(e.g. a custom target) and add a dependency from `cargo-prebuild_` to your +custom target for generating the bindings. + +Example: + +```cmake +# Import your Rust targets +corrosion_import_crate(MANIFEST_PATH rust/Cargo.toml) +# Link C/C++ libraries with your Rust target +corrosion_link_libraries(target_name c_library) +# Optionally explicitly define which linker to use. +corrosion_set_linker(target_name your_custom_linker) +# Optionally set linker arguments +corrosion_add_target_local_rustflags(target_name "-Clink-args=") +# Optionally tell CMake that the rust crate depends on another target (e.g. a code generator) +add_dependencies(cargo-prebuild_ custom_bindings_target) +``` + +### Cross Compiling +Corrosion attempts to support cross-compiling as generally as possible, though not all +configurations are tested. Cross-compiling is explicitly supported in the following scenarios. + +In all cases, you will need to install the standard library for the Rust target triple. When using +Rustup, you can use it to install the target standard library: + +```bash +rustup target add +``` + +If the target triple is automatically derived, Corrosion will print the target during configuration. +For example: + +``` +-- Rust Target: aarch64-linux-android +``` + +#### Windows-to-Windows +Corrosion supports cross-compiling between arbitrary Windows architectures using the Visual Studio +Generator. For example, to cross-compile for ARM64 from any platform, simply set the `-A` +architecture flag: + +```bash +cmake -S. -Bbuild-arm64 -A ARM64 +cmake --build build-arm64 +``` + +Please note that for projects containing a build-script at least Rust 1.54 is required due to a bug +in previous cargo versions, which causes the build-script to incorrectly be built for the target +platform. + +#### Linux-to-Linux +In order to cross-compile on Linux, you will need to install a cross-compiler. For example, on +Ubuntu, to cross compile for 64-bit Little-Endian PowerPC Little-Endian, install +`g++-powerpc64le-linux-gnu` from apt-get: + +```bash +sudo apt install g++-powerpc64le-linux-gnu +``` + +Currently, Corrosion does not automatically determine the target triple while cross-compiling on +Linux, so you'll need to specify a matching `Rust_CARGO_TARGET`. + +```bash +cmake -S. -Bbuild-ppc64le -DRust_CARGO_TARGET=powerpc64le-unknown-linux-gnu -DCMAKE_CXX_COMPILER=powerpc64le-linux-gnu-g++ +cmake --build build-ppc64le +``` + +#### Android + +Cross-compiling for Android is supported on all platforms with the Makefile and Ninja generators, +and the Rust target triple will automatically be selected. The CMake +[cross-compiling instructions for Android](https://cmake.org/cmake/help/latest/manual/cmake-toolchains.7.html#cross-compiling-for-android) +apply here. For example, to build for ARM64: + +```bash +cmake -S. -Bbuild-android-arm64 -GNinja -DCMAKE_SYSTEM_NAME=Android \ + -DCMAKE_ANDROID_NDK=/path/to/android-ndk-rxxd -DCMAKE_ANDROID_ARCH_ABI=arm64-v8a +``` + +**Important note:** The Android SDK ships with CMake 3.10 at newest, which Android Studio will +prefer over any CMake you've installed locally. CMake 3.10 is insufficient for using Corrosion, +which requires a minimum of CMake 3.22. If you're using Android Studio to build your project, +follow the instructions in the Android Studio documentation for +[using a specific version of CMake](https://developer.android.com/studio/projects/install-ndk#vanilla_cmake). + + +### CMake `OUTPUT_DIRECTORY` target properties and `IMPORTED_LOCATION` + +Corrosion respects the following `OUTPUT_DIRECTORY` target properties: +- [ARCHIVE_OUTPUT_DIRECTORY](https://cmake.org/cmake/help/latest/prop_tgt/ARCHIVE_OUTPUT_DIRECTORY.html) +- [LIBRARY_OUTPUT_DIRECTORY](https://cmake.org/cmake/help/latest/prop_tgt/LIBRARY_OUTPUT_DIRECTORY.html) +- [RUNTIME_OUTPUT_DIRECTORY](https://cmake.org/cmake/help/latest/prop_tgt/RUNTIME_OUTPUT_DIRECTORY.html) +- [PDB_OUTPUT_DIRECTORY](https://cmake.org/cmake/help/latest/prop_tgt/PDB_OUTPUT_DIRECTORY.html) + +If the target property is set (e.g. by defining the `CMAKE_XYZ_OUTPUT_DIRECTORY` variable before calling +`corrosion_import_crate()`), corrosion will copy the built rust artifacts to the location defined in the +target property. +Due to limitations in CMake these target properties are evaluated in a deferred manner, to +support the user setting the target properties after the call to `corrosion_import_crate()`. +This has the side effect that the `IMPORTED_LOCATION` property will be set late, and users should not +use `get_property` to read `IMPORTED_LOCATION` at configure time. Instead, generator expressions +should be used to get the location of the target artifact. +If `IMPORTED_LOCATION` is needed at configure time users may use `cmake_language(DEFER CALL ...)` to defer +evaluation to after the `IMPORTED_LOCATION` property is set. diff -r 2b4f361e3891 -r 629d5123a979 tools/corrosion/test/CMakeLists.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/corrosion/test/CMakeLists.txt Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,188 @@ +# This option is currently used to prevent recursion +option(CORROSION_TESTS "Enable Corrosion tests" ON) +mark_as_advanced(CORROSION_TESTS) +if(NOT CORROSION_TESTS) + return() +endif() + +option(CORROSION_TESTS_CXXBRIDGE + "Build cxxbridge tests which requires cxxbridge executable being available" + OFF) +option(CORROSION_TESTS_KEEP_BUILDDIRS + "By default corrosion tests will cleanup after themselves. This option limits the cleaning up to the + target directories and will keep the build directories, which may be useful for caching." + OFF) +mark_as_advanced(CORROSION_TESTS_NO_CLEANUP) + +set(test_install_path "${CMAKE_CURRENT_BINARY_DIR}/test-install-corrosion") +file(REAL_PATH "${CMAKE_CURRENT_SOURCE_DIR}/.." corrosion_source_dir) +set(test_header_contents + "option(CORROSION_TESTS_FIND_CORROSION \"Use Corrosion as a subdirectory\" OFF)" + "if (CORROSION_TESTS_FIND_CORROSION)" + " set(CMAKE_PREFIX_PATH \"${test_install_path}\" CACHE INTERNAL \"\" FORCE)" + " find_package(Corrosion REQUIRED PATHS \"${test_install_path}\" NO_CMAKE_SYSTEM_PATH)" + "else()" + " add_subdirectory(\"${corrosion_source_dir}\" corrosion)" + "endif()" +) + +string(REPLACE ";" "\n" test_header_contents "${test_header_contents}") + +file(WRITE test_header.cmake "${test_header_contents}") + +option(CORROSION_TESTS_INSTALL_CORROSION + "Install Corrosion to a test directory and let tests use the installed Corrosion" + OFF) +if(CORROSION_TESTS_INSTALL_CORROSION) + add_test(NAME "install_corrosion_configure" + COMMAND + ${CMAKE_COMMAND} + -S "${CMAKE_CURRENT_SOURCE_DIR}/.." + -B "${CMAKE_CURRENT_BINARY_DIR}/build-corrosion" + -DCORROSION_VERBOSE_OUTPUT=ON + -DCORROSION_TESTS=OFF + -DCMAKE_BUILD_TYPE=Release + -G${CMAKE_GENERATOR} + "-DCMAKE_INSTALL_PREFIX=${test_install_path}" + ) + add_test(NAME "install_corrosion_build" + COMMAND + ${CMAKE_COMMAND} --build "${CMAKE_CURRENT_BINARY_DIR}/build-corrosion" --config Release + ) + add_test(NAME "install_corrosion_install" + COMMAND + ${CMAKE_COMMAND} --install "${CMAKE_CURRENT_BINARY_DIR}/build-corrosion" --config Release + ) + set_tests_properties("install_corrosion_configure" PROPERTIES FIXTURES_SETUP "fixture_corrosion_configure") + set_tests_properties("install_corrosion_build" PROPERTIES FIXTURES_SETUP "fixture_corrosion_build") + set_tests_properties("install_corrosion_build" PROPERTIES FIXTURES_REQUIRED "fixture_corrosion_configure") + set_tests_properties("install_corrosion_install" PROPERTIES FIXTURES_REQUIRED "fixture_corrosion_build") + set_tests_properties("install_corrosion_install" PROPERTIES FIXTURES_SETUP "fixture_corrosion_install") + + add_test(NAME "install_corrosion_build_cleanup" COMMAND "${CMAKE_COMMAND}" -E remove_directory "${CMAKE_CURRENT_BINARY_DIR}/build-corrosion") + set_tests_properties("install_corrosion_build_cleanup" PROPERTIES + FIXTURES_CLEANUP + "fixture_corrosion_configure;fixture_corrosion_build" + ) + + add_test(NAME "install_corrosion_cleanup" COMMAND "${CMAKE_COMMAND}" -E remove_directory "${test_install_path}") + set_tests_properties("install_corrosion_cleanup" PROPERTIES + FIXTURES_CLEANUP + "fixture_corrosion_configure;fixture_corrosion_build;fixture_corrosion_install" + ) +endif() + +function(corrosion_tests_add_test test_name bin_names) + set(options "IS_HOSTBUILD") + set(one_value_kewords "TEST_SRC_DIR") + set(multi_value_keywords "") + cmake_parse_arguments(PARSE_ARGV 2 TST "${options}" "${one_value_kewords}" "${multi_value_keywords}") + set(pass_through_arguments "${TST_UNPARSED_ARGUMENTS}") + +# In the future we could add multiple tests here for different configurations (generator, build mode, rust version ...) +# which would allow us to simplify the github job matrix + if(TST_TEST_SRC_DIR) + set(test_dir "${TST_TEST_SRC_DIR}") + else() + set(test_dir "${test_name}") + endif() + + set(configure_cmake_args) + if(CMAKE_C_COMPILER) + list(APPEND configure_cmake_args "C_COMPILER" "${CMAKE_C_COMPILER}") + endif() + if(CMAKE_CXX_COMPILER) + list(APPEND configure_cmake_args "CXX_COMPILER" "${CMAKE_CXX_COMPILER}") + endif() + if(CMAKE_C_COMPILER_TARGET) + list(APPEND configure_cmake_args "C_COMPILER_TARGET" "${CMAKE_C_COMPILER_TARGET}") + endif() + if(CMAKE_CXX_COMPILER_TARGET) + list(APPEND configure_cmake_args "CXX_COMPILER_TARGET" "${CMAKE_CXX_COMPILER_TARGET}") + endif() + if(CMAKE_GENERATOR_PLATFORM) + list(APPEND configure_cmake_args "GENERATOR_PLATFORM" "${CMAKE_GENERATOR_PLATFORM}") + endif() + if(CMAKE_CROSSCOMPILING) + list(APPEND configure_cmake_args SYSTEM_NAME "${CMAKE_SYSTEM_NAME}") + endif() + if(CMAKE_OSX_ARCHITECTURES) + list(APPEND configure_cmake_args OSX_ARCHITECTURES "${CMAKE_OSX_ARCHITECTURES}") + endif() + if(CMAKE_TOOLCHAIN_FILE) + list(APPEND configure_cmake_args TOOLCHAIN_FILE "${CMAKE_TOOLCHAIN_FILE}") + endif() + + add_test(NAME "${test_name}_build" + COMMAND + ${CMAKE_COMMAND} + -P "${CMAKE_SOURCE_DIR}/test/ConfigureAndBuild.cmake" + SOURCE_DIR "${CMAKE_CURRENT_LIST_DIR}/${test_dir}" + BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}/build-${test_name}" + GENERATOR "${CMAKE_GENERATOR}" + RUST_TOOLCHAIN "${Rust_TOOLCHAIN}" + CARGO_TARGET "${Rust_CARGO_TARGET}" + ${configure_cmake_args} + ${pass_through_arguments} + + COMMAND_EXPAND_LISTS + ) + set_tests_properties("${test_name}_build" PROPERTIES FIXTURES_SETUP "build_fixture_${test_name}") + if(CORROSION_TESTS_INSTALL_CORROSION) + set_tests_properties("${test_name}_build" PROPERTIES FIXTURES_REQUIRED "fixture_corrosion_install") + endif() + foreach(bin ${bin_names}) + if(WIN32) + set(bin_filename "${bin}.exe") + else() + set(bin_filename "${bin}") + endif() + add_test(NAME "${test_name}_run_${bin}" COMMAND "${CMAKE_CURRENT_BINARY_DIR}/build-${test_name}/${bin_filename}") + set_tests_properties("${test_name}_run_${bin}" PROPERTIES FIXTURES_REQUIRED "build_fixture_${test_name}") + # CMAKE_CROSSCOMPILING is not set when cross-compiling with VS (via -A flag). + # Todo: We could run x86 binaries on x64 hosts. + if((CMAKE_CROSSCOMPILING OR CMAKE_VS_PLATFORM_NAME) AND NOT "${TST_IS_HOSTBUILD}") + # Todo: In the future we could potentially run some tests with qemu. + set_tests_properties("${test_name}_run_${bin}" PROPERTIES DISABLED TRUE) + endif() + endforeach() + + if(CORROSION_TESTS_KEEP_BUILDDIRS) + add_test(NAME "${test_name}_cleanup_artifacts" + COMMAND "${CMAKE_COMMAND}" --build "${CMAKE_CURRENT_BINARY_DIR}/build-${test_name}" --target clean + ) + add_test(NAME "${test_name}_cleanup_cargo" + COMMAND "${CMAKE_COMMAND}" -E remove_directory "${CMAKE_CURRENT_BINARY_DIR}/build-${test_name}/cargo" + ) + set_tests_properties("${test_name}_cleanup_artifacts" PROPERTIES FIXTURES_CLEANUP "build_fixture_${test_name}") + set_tests_properties("${test_name}_cleanup_cargo" PROPERTIES FIXTURES_CLEANUP "build_fixture_${test_name}") + else() + add_test(NAME "${test_name}_cleanup" COMMAND "${CMAKE_COMMAND}" -E remove_directory "${CMAKE_CURRENT_BINARY_DIR}/build-${test_name}") + set_tests_properties("${test_name}_cleanup" PROPERTIES FIXTURES_CLEANUP "build_fixture_${test_name}") + endif() +endfunction() + +# Please keep this in alphabetical order. +add_subdirectory(cargo_flags) +add_subdirectory(cpp2rust) +if(Rust_VERSION VERSION_GREATER_EQUAL "1.64.0") + # Flag `--crate-type` is only supported since Rust 1.64.0 + add_subdirectory(crate_type) + add_subdirectory(override_crate_type) +endif() +add_subdirectory(custom_profiles) +add_subdirectory(cbindgen) +add_subdirectory(corrosion_install) +add_subdirectory(cxxbridge) +add_subdirectory(envvar) +add_subdirectory(features) +add_subdirectory(find_rust) +add_subdirectory(gensource) +add_subdirectory(hostbuild) +add_subdirectory(multitarget) +add_subdirectory(nostd) +add_subdirectory("output directory") +add_subdirectory(parse_target_triple) +add_subdirectory(rust2cpp) +add_subdirectory(rustflags) +add_subdirectory(workspace) diff -r 2b4f361e3891 -r 629d5123a979 tools/corrosion/test/ConfigureAndBuild.cmake --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/corrosion/test/ConfigureAndBuild.cmake Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,118 @@ +# CMake script to configure and build a test project + +set(TEST_ARG_LIST) + +# Expect actual arguments to start at index 3 (cmake -P ) +foreach(ARG_INDEX RANGE 3 ${CMAKE_ARGC}) + list(APPEND TEST_ARG_LIST "${CMAKE_ARGV${ARG_INDEX}}") +endforeach() + +set(options "USE_INSTALLED_CORROSION") +set(oneValueArgs + SOURCE_DIR + BINARY_DIR + GENERATOR + GENERATOR_PLATFORM + RUST_TOOLCHAIN + CARGO_TARGET + C_COMPILER + CXX_COMPILER + C_COMPILER_TARGET + CXX_COMPILER_TARGET + SYSTEM_NAME + CARGO_PROFILE + OSX_ARCHITECTURES + TOOLCHAIN_FILE +) +set(multiValueArgs "PASS_THROUGH_ARGS") +cmake_parse_arguments(TEST "${options}" "${oneValueArgs}" + "${multiValueArgs}" ${TEST_ARG_LIST} ) + +set(configure_args "") +if(TEST_CARGO_TARGET) + list(APPEND configure_args "-DRust_CARGO_TARGET=${TEST_CARGO_TARGET}") +endif() +if(TEST_USE_INSTALLED_CORROSION) + list(APPEND configure_args "-DCORROSION_TESTS_FIND_CORROSION=ON") +endif() +if(TEST_GENERATOR_PLATFORM) + list(APPEND configure_args "-A${TEST_GENERATOR_PLATFORM}") +endif() +if(TEST_C_COMPILER) + list(APPEND configure_args "-DCMAKE_C_COMPILER=${TEST_C_COMPILER}") +endif() +if(TEST_CXX_COMPILER) + list(APPEND configure_args "-DCMAKE_CXX_COMPILER=${TEST_CXX_COMPILER}") +endif() +if(TEST_C_COMPILER_TARGET) + list(APPEND configure_args "-DCMAKE_C_COMPILER_TARGET=${TEST_C_COMPILER_TARGET}") +endif() +if(TEST_CXX_COMPILER_TARGET) + list(APPEND configure_args "-DCMAKE_CXX_COMPILER_TARGET=${TEST_CXX_COMPILER_TARGET}") +endif() +if(TEST_SYSTEM_NAME) + list(APPEND configure_args "-DCMAKE_SYSTEM_NAME=${TEST_SYSTEM_NAME}") +endif() +if(TEST_OSX_ARCHITECTURES) + list(APPEND configure_args "-DCMAKE_OSX_ARCHITECTURES=${TEST_OSX_ARCHITECTURES}") +endif() +if(TEST_TOOLCHAIN_FILE) + list(APPEND configure_args "-DCMAKE_TOOLCHAIN_FILE=${TEST_TOOLCHAIN_FILE}") +endif() +if(TEST_CARGO_PROFILE) + list(APPEND configure_args "-DCARGO_PROFILE=${TEST_CARGO_PROFILE}") +endif() + +# Remove old binary directory +file(REMOVE_RECURSE "${TEST_BINARY_DIR}") + +file(MAKE_DIRECTORY "${TEST_BINARY_DIR}") + +message(STATUS "TEST_BINARY_DIRECTORY: ${TEST_BINARY_DIR}") + +execute_process( + COMMAND + "${CMAKE_COMMAND}" + "-G${TEST_GENERATOR}" + "-DRust_TOOLCHAIN=${TEST_RUST_TOOLCHAIN}" + --log-level Debug + ${configure_args} + ${TEST_PASS_THROUGH_ARGS} + -S "${TEST_SOURCE_DIR}" + -B "${TEST_BINARY_DIR}" + COMMAND_ECHO STDOUT + RESULT_VARIABLE EXIT_CODE +) + +if (NOT "${EXIT_CODE}" EQUAL 0) + message(FATAL_ERROR "Configure step failed. Exit code: `${EXIT_CODE}`") +endif() + +if ("${TEST_GENERATOR}" STREQUAL "Ninja Multi-Config" + OR "${TEST_GENERATOR}" MATCHES "Visual Studio" + ) + foreach(config Debug Release RelWithDebInfo) + execute_process( + COMMAND "${CMAKE_COMMAND}" + --build "${TEST_BINARY_DIR}" + --config "${config}" + COMMAND_ECHO STDOUT + RESULT_VARIABLE EXIT_CODE + ) + if (NOT "${EXIT_CODE}" EQUAL 0) + message(FATAL_ERROR "Build step failed for config `${config}`. " + "Exit code: `${EXIT_CODE}`") + endif() + endforeach() +else() + execute_process( + COMMAND "${CMAKE_COMMAND}" --build "${TEST_BINARY_DIR}" + COMMAND_ECHO STDOUT + RESULT_VARIABLE EXIT_CODE + ) + if (NOT "${EXIT_CODE}" EQUAL 0) + message(FATAL_ERROR "Build step failed. Exit code: `${EXIT_CODE}`") + endif() +endif() + + diff -r 2b4f361e3891 -r 629d5123a979 tools/corrosion/test/README.md --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/corrosion/test/README.md Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,7 @@ +# Corrosion Tests + +Corrosions tests are run via ctest. The tests themselves utilize CMake script mode +to configure and build a test project, which allows for great flexibility. +Using ctest properties such as `PASS_REGULAR_EXPRESSION` or `FAIL_REGULAR_EXPRESSION` +can be used to confirm that built executable targets run as expected, but can also +be used to fail tests if Corrosion warnings appear in the configure output. \ No newline at end of file diff -r 2b4f361e3891 -r 629d5123a979 tools/corrosion/test/cargo_flags/CMakeLists.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/corrosion/test/cargo_flags/CMakeLists.txt Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,3 @@ +corrosion_tests_add_test(cargo_flags "flags-exe") + +set_tests_properties("cargo_flags_run_flags-exe" PROPERTIES PASS_REGULAR_EXPRESSION [[Hello, Cxx! I am Rust!]]) diff -r 2b4f361e3891 -r 629d5123a979 tools/corrosion/test/cargo_flags/cargo_flags/CMakeLists.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/corrosion/test/cargo_flags/cargo_flags/CMakeLists.txt Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,16 @@ +cmake_minimum_required(VERSION 3.15) +project(test_project VERSION 0.1.0) +include(../../test_header.cmake) + +corrosion_import_crate(MANIFEST_PATH rust/Cargo.toml FLAGS --features one) + +add_executable(flags-exe main.cpp) +target_link_libraries(flags-exe PUBLIC flags_lib) +corrosion_set_cargo_flags(flags_lib --features two) +corrosion_set_cargo_flags(flags_lib $) + +set_property( + TARGET flags_lib + APPEND + PROPERTY more_flags --features three +) diff -r 2b4f361e3891 -r 629d5123a979 tools/corrosion/test/cargo_flags/cargo_flags/main.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/corrosion/test/cargo_flags/cargo_flags/main.cpp Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,6 @@ +extern "C" void rust_function(char const *name); + + +int main(int argc, char **argv) { + rust_function("Cxx"); +} diff -r 2b4f361e3891 -r 629d5123a979 tools/corrosion/test/cargo_flags/cargo_flags/rust/Cargo.toml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/corrosion/test/cargo_flags/cargo_flags/rust/Cargo.toml Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,13 @@ +[package] +name = "flags-lib" +version = "0.1.0" +edition = "2018" + +[lib] +crate-type=["staticlib"] + +[features] + +one = [] +two = [] +three = [] diff -r 2b4f361e3891 -r 629d5123a979 tools/corrosion/test/cargo_flags/cargo_flags/rust/src/lib.rs --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/corrosion/test/cargo_flags/cargo_flags/rust/src/lib.rs Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,14 @@ +use std::os::raw::c_char; + +#[no_mangle] +pub extern "C" fn rust_function(name: *const c_char) { + let name = unsafe { std::ffi::CStr::from_ptr(name).to_str().unwrap() }; + println!("Hello, {}! I am Rust!", name); + + #[cfg(not(feature = "one"))] + compile_error!("Feature one is not enabled"); + #[cfg(not(feature = "two"))] + compile_error!("Feature two is not enabled"); + #[cfg(not(feature = "three"))] + compile_error!("Feature three is not enabled"); +} diff -r 2b4f361e3891 -r 629d5123a979 tools/corrosion/test/cbindgen/CMakeLists.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/corrosion/test/cbindgen/CMakeLists.txt Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,11 @@ +corrosion_tests_add_test(cbindgen_rust2cpp "cpp-exe" TEST_SRC_DIR rust2cpp) + +set_tests_properties(cbindgen_rust2cpp_run_cpp-exe PROPERTIES PASS_REGULAR_EXPRESSION + "^add_point Result: Point { x: 100, y: 100 }\r?\n$" +) +# Todo: We also should add a cpp2rust test with the following setup: +# - A rust lib that is used by a rust executable +# - cbindgen creates bindings for the rust-lib +# - c++ code uses the rust lib and is used in turn by the rust bin. + +# todo: add a test for the DEPFILE and correct regenerating if the sources are touched. diff -r 2b4f361e3891 -r 629d5123a979 tools/corrosion/test/cbindgen/rust2cpp/CMakeLists.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/corrosion/test/cbindgen/rust2cpp/CMakeLists.txt Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,10 @@ +cmake_minimum_required(VERSION 3.15) +project(test_project VERSION 0.1.0) +include(../../test_header.cmake) + +corrosion_import_crate(MANIFEST_PATH rust/Cargo.toml) +corrosion_experimental_cbindgen(TARGET rust_lib HEADER_NAME "rust-lib.h") + +add_executable(cpp-exe main.cpp) +set_property(TARGET cpp-exe PROPERTY CXX_STANDARD 11) +target_link_libraries(cpp-exe PUBLIC rust_lib) diff -r 2b4f361e3891 -r 629d5123a979 tools/corrosion/test/cbindgen/rust2cpp/main.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/corrosion/test/cbindgen/rust2cpp/main.cpp Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,22 @@ +#include "rust-lib.h" +#include + +int main(int argc, char **argv) { + assert(is_magic_number(MAGIC_NUMBER)); + struct Point p1, p2; + p1.x = 54; + p2.x = 46; + p1.y = 34; + p2.y = 66; + add_point(&p1, &p2); + assert(p1.x == 100); + assert(p2.x == 46); + assert(p1.y == 100); + assert(p2.y == 66); + add_point(&p1, NULL); + assert(p1.x == 100); + assert(p1.y == 100); + + assert(OTHER_MOD_MAGIC_NUMBER == 192312312); + assert(FFI_MAGIC_NUMBER == 0xFDA00184); +} diff -r 2b4f361e3891 -r 629d5123a979 tools/corrosion/test/cbindgen/rust2cpp/rust/Cargo.toml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/corrosion/test/cbindgen/rust2cpp/rust/Cargo.toml Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,10 @@ +[package] +name = "rust-lib" +version = "0.1.0" +license = "MIT" +edition = "2018" + +[dependencies] + +[lib] +crate-type=["staticlib"] diff -r 2b4f361e3891 -r 629d5123a979 tools/corrosion/test/cbindgen/rust2cpp/rust/cbindgen.toml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/corrosion/test/cbindgen/rust2cpp/rust/cbindgen.toml Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,4 @@ +language = "C++" +include_version = true + + diff -r 2b4f361e3891 -r 629d5123a979 tools/corrosion/test/cbindgen/rust2cpp/rust/src/ffi.rs --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/corrosion/test/cbindgen/rust2cpp/rust/src/ffi.rs Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,3 @@ +//! Just a module that contains some entries that should be parsed by cbindgen. + +pub const FFI_MAGIC_NUMBER: u64 = 0xFDA0_0184; diff -r 2b4f361e3891 -r 629d5123a979 tools/corrosion/test/cbindgen/rust2cpp/rust/src/lib.rs --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/corrosion/test/cbindgen/rust2cpp/rust/src/lib.rs Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,33 @@ +pub const MAGIC_NUMBER: u64 = 0xABCD_EFAB; + +pub mod ffi; +pub mod other_mod; + +#[derive(Debug)] +#[repr(C)] +pub struct Point { + x: u64, + y: u64, +} + +impl Point { + pub(crate) fn add(&mut self, rhs: &Point) { + self.x = self.x.wrapping_add(rhs.x); + self.y = self.y.wrapping_add(rhs.y); + } +} + +#[no_mangle] +pub extern "C" fn add_point(lhs: Option<&mut Point>, rhs: Option<&Point>) { + if let (Some(p1), Some(p2)) = (lhs, rhs) { + p1.add(p2); + // Print something so we can let Ctest assert the output. + println!("add_point Result: {:?}", p1); + } +} + +// simple test if the constant was exported by cbindgen correctly +#[no_mangle] +pub extern "C" fn is_magic_number(num: u64) -> bool { + num == MAGIC_NUMBER +} diff -r 2b4f361e3891 -r 629d5123a979 tools/corrosion/test/cbindgen/rust2cpp/rust/src/other_mod/mod.rs --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/corrosion/test/cbindgen/rust2cpp/rust/src/other_mod/mod.rs Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,1 @@ +pub const OTHER_MOD_MAGIC_NUMBER: u32 = 192312312; diff -r 2b4f361e3891 -r 629d5123a979 tools/corrosion/test/corrosion_install/CMakeLists.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/corrosion/test/corrosion_install/CMakeLists.txt Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,27 @@ +if(NOT (CMAKE_CROSSCOMPILING AND MSVC)) + # When using MSVC the cmake build via ExternalProject seems to inherit the target architecture, + # which breaks the test. Since we practically don't care about this, and we just want to ensure + # that installing an executable works, skipping this test when cross-compiling is fine. + corrosion_tests_add_test(install_rust_bin "generated_from_installed_bin") + + set_tests_properties("install_rust_bin_run_generated_from_installed_bin" + PROPERTIES PASS_REGULAR_EXPRESSION + "Hello World! I'm generated code" + ) +endif() + +# Todo: Fix and re-enable tests on Windows +if(NOT CMAKE_CROSSCOMPILING AND NOT WIN32) + corrosion_tests_add_test(install_lib "main-static;main-shared") + + set_tests_properties("install_lib_run_main-static" "install_lib_run_main-shared" + PROPERTIES PASS_REGULAR_EXPRESSION + "The sum is 11" + ) +endif() + +# Further tests we should add: +# - Test installing a Rust executable, that requires a (C/C++) shared library at runtime. +# Note: We should delete the build directory of the subproject +# before running the installed rust executable, to insure the shared library is loaded from the +# installed location and not from the build dir. \ No newline at end of file diff -r 2b4f361e3891 -r 629d5123a979 tools/corrosion/test/corrosion_install/install_lib/CMakeLists.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/corrosion/test/corrosion_install/install_lib/CMakeLists.txt Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,44 @@ +cmake_minimum_required(VERSION 3.15) +project(test_project VERSION 0.1.0) +include(ExternalProject) + +add_library(static_lib STATIC IMPORTED) +add_library(shared_lib SHARED IMPORTED) +set(install_prefix "${CMAKE_CURRENT_BINARY_DIR}/rust_lib") +set(static_lib_install_path "${install_prefix}/lib/${CMAKE_STATIC_LIBRARY_PREFIX}rust_lib${CMAKE_STATIC_LIBRARY_SUFFIX}") +set(shared_lib_install_path "${install_prefix}/lib/${CMAKE_SHARED_LIBRARY_PREFIX}rust_lib${CMAKE_SHARED_LIBRARY_SUFFIX}") + + +set_target_properties(static_lib PROPERTIES + IMPORTED_LOCATION + "${static_lib_install_path}") + +set_target_properties(shared_lib PROPERTIES + IMPORTED_LOCATION + "${shared_lib_install_path}") + +add_executable(main-static main.cpp) +target_link_libraries(main-static PRIVATE static_lib) + +ExternalProject_Add( + rust_lib + PREFIX "${CMAKE_CURRENT_BINARY_DIR}/rust_lib" + SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/rust_lib" + CMAKE_ARGS "-DCMAKE_INSTALL_PREFIX=${install_prefix}" + # INSTALL_BYPRODUCTS "${static_lib_install_path}" +) + +# Dummy target since INSTALL_BYPRODUCTS requires CMake 3.26 +add_custom_target(build_rust_project_dummy + COMMAND echo dummy + BYPRODUCTS "${static_lib_install_path}" "${shared_lib_install_path}" + DEPENDS rust_lib) + +add_dependencies(main-static build_rust_project_dummy) + +set(CMAKE_BUILD_RPATH ${install_prefix}/lib) +add_executable(main-shared main.cpp) +target_link_libraries(main-shared + PUBLIC shared_lib) + +add_dependencies(main-shared build_rust_project_dummy) diff -r 2b4f361e3891 -r 629d5123a979 tools/corrosion/test/corrosion_install/install_lib/main.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/corrosion/test/corrosion_install/install_lib/main.cpp Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,11 @@ +#include +#include +#include + +extern "C" uint64_t add(uint64_t a, uint64_t b); + +int main(int argc, char **argv) { + uint64_t sum = add(5, 6); + assert(sum == 11); + std::cout << "The sum is " << sum << std::endl; +} diff -r 2b4f361e3891 -r 629d5123a979 tools/corrosion/test/corrosion_install/install_lib/rust_lib/CMakeLists.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/corrosion/test/corrosion_install/install_lib/rust_lib/CMakeLists.txt Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,18 @@ +cmake_minimum_required(VERSION 3.15) +project(test_project VERSION 0.1.0) +include(../../../test_header.cmake) + +corrosion_import_crate(MANIFEST_PATH Cargo.toml) + +if("${CMAKE_SYSTEM_NAME}" STREQUAL "Linux") + corrosion_add_target_local_rustflags(rust_lib "-Clink-arg=-Wl,-soname,librust_lib.so") + set_target_properties(rust_lib-shared PROPERTIES IMPORTED_SONAME librust_lib.so) +elseif("${CMAKE_SYSTEM_NAME}" STREQUAL "Darwin") + corrosion_add_target_local_rustflags(rust_lib -Clink-arg=-Wl,-install_name,@rpath/librust_lib.dylib,-current_version,1.0,-compatibility_version,1.0) + set_target_properties(rust_lib-shared PROPERTIES IMPORTED_NO_SONAME 0) + set_target_properties(rust_lib-shared PROPERTIES IMPORTED_SONAME librust_lib.dylib) +endif() + +target_sources(rust_lib INTERFACE include/rust_lib/rust_lib.hpp) + +corrosion_install(TARGETS rust_lib) diff -r 2b4f361e3891 -r 629d5123a979 tools/corrosion/test/corrosion_install/install_lib/rust_lib/Cargo.toml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/corrosion/test/corrosion_install/install_lib/rust_lib/Cargo.toml Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,9 @@ +[package] +name = "rust_lib" +version = "0.1.0" +edition = "2018" + +[dependencies] + +[lib] +crate-type = ["staticlib", "cdylib"] diff -r 2b4f361e3891 -r 629d5123a979 tools/corrosion/test/corrosion_install/install_lib/rust_lib/include/rust_lib/rust_lib.hpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/corrosion/test/corrosion_install/install_lib/rust_lib/include/rust_lib/rust_lib.hpp Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,3 @@ +#include + +extern "C" uint64_t add(uint64_t left, uint64_t right); diff -r 2b4f361e3891 -r 629d5123a979 tools/corrosion/test/corrosion_install/install_lib/rust_lib/src/lib.rs --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/corrosion/test/corrosion_install/install_lib/rust_lib/src/lib.rs Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,5 @@ + +#[no_mangle] +pub extern "C" fn add(left: u64, right: u64) -> u64 { + left + right +} diff -r 2b4f361e3891 -r 629d5123a979 tools/corrosion/test/corrosion_install/install_rust_bin/CMakeLists.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/corrosion/test/corrosion_install/install_rust_bin/CMakeLists.txt Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,32 @@ +cmake_minimum_required(VERSION 3.22) +project(test_project VERSION 0.1.0) + +# Note: Corrosion supports `hostbuild`, so building a Rust binary in a subproject +# like this doesn't offer any benefit over using the hostbuild option. +# However, this is a reasonable way to test that installing Rust binaries via +# corrosion_install works as expected. +include(ExternalProject) + +set(bin_suffix "") +if(CMAKE_HOST_WIN32) + set(bin_suffix ".exe") +endif() +set(generator_bin_path "${CMAKE_CURRENT_BINARY_DIR}/rust_bin/bin/my_rust_bin${bin_suffix}") + +ExternalProject_Add( + rust_bin + PREFIX "${CMAKE_CURRENT_BINARY_DIR}/rust_bin" + SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/rust_bin" + CMAKE_ARGS "-DCMAKE_INSTALL_PREFIX=${CMAKE_CURRENT_BINARY_DIR}/rust_bin" +) + +# This custom command is the main part of the test: +# We test that corrosion (in the CMake of the ExternalProject) properly installed +# a Rust executable to the location we specified by running the executable, which generates some cpp code. +add_custom_command( + OUTPUT generated_main.cpp + COMMAND "${generator_bin_path}" > "${CMAKE_CURRENT_BINARY_DIR}/generated_main.cpp" + DEPENDS rust_bin +) + +add_executable(generated_from_installed_bin ${CMAKE_CURRENT_BINARY_DIR}/generated_main.cpp) diff -r 2b4f361e3891 -r 629d5123a979 tools/corrosion/test/corrosion_install/install_rust_bin/rust_bin/CMakeLists.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/corrosion/test/corrosion_install/install_rust_bin/rust_bin/CMakeLists.txt Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,7 @@ +cmake_minimum_required(VERSION 3.22) +project(test_rust_bin VERSION 0.1.0) +include(../../../test_header.cmake) +include(GNUInstallDirs) + +corrosion_import_crate(MANIFEST_PATH Cargo.toml) +corrosion_install(TARGETS my_rust_bin) diff -r 2b4f361e3891 -r 629d5123a979 tools/corrosion/test/corrosion_install/install_rust_bin/rust_bin/Cargo.toml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/corrosion/test/corrosion_install/install_rust_bin/rust_bin/Cargo.toml Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,7 @@ +[package] +name = "my_rust_bin" +version = "0.1.0" +edition = "2018" +license = "MIT" + +[dependencies] diff -r 2b4f361e3891 -r 629d5123a979 tools/corrosion/test/corrosion_install/install_rust_bin/rust_bin/src/main.rs --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/corrosion/test/corrosion_install/install_rust_bin/rust_bin/src/main.rs Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,9 @@ +fn main() { + println!( +"#include +int main() {{ + std::cout << \"Hello World! I'm generated code\"; + return 0; +}}" + ); +} diff -r 2b4f361e3891 -r 629d5123a979 tools/corrosion/test/cpp2rust/CMakeLists.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/corrosion/test/cpp2rust/CMakeLists.txt Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,5 @@ +corrosion_tests_add_test(cpp2rust "rust-exe") + +set_tests_properties("cpp2rust_run_rust-exe" PROPERTIES PASS_REGULAR_EXPRESSION + "Hello, Rust! I am Cpp!\r?\nHello, Rust! I am Cpp library Number 2!\r?\nHello, Rust! I am Cpp library Number 3!" + ) diff -r 2b4f361e3891 -r 629d5123a979 tools/corrosion/test/cpp2rust/cpp2rust/CMakeLists.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/corrosion/test/cpp2rust/cpp2rust/CMakeLists.txt Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,32 @@ +cmake_minimum_required(VERSION 3.15) +project(test_project VERSION 0.1.0) +include(../../test_header.cmake) + +corrosion_import_crate(MANIFEST_PATH rust/Cargo.toml) + +add_library(cpp-lib lib.cpp) +target_compile_features(cpp-lib PRIVATE cxx_std_14) +set_target_properties( + cpp-lib + PROPERTIES + POSITION_INDEPENDENT_CODE ON +) + +add_library(cpp-lib2 lib2.cpp) +target_compile_features(cpp-lib2 PRIVATE cxx_std_14) +set_target_properties( + cpp-lib2 + PROPERTIES + POSITION_INDEPENDENT_CODE ON + OUTPUT_NAME cpp-lib-renamed +) + +add_library(cpp-lib3 "path with space/lib3.cpp" ) +target_compile_features(cpp-lib3 PRIVATE cxx_std_14) +set_target_properties( + cpp-lib3 + PROPERTIES + POSITION_INDEPENDENT_CODE ON +) + +corrosion_link_libraries(rust-exe cpp-lib cpp-lib2 cpp-lib3) diff -r 2b4f361e3891 -r 629d5123a979 tools/corrosion/test/cpp2rust/cpp2rust/lib.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/corrosion/test/cpp2rust/cpp2rust/lib.cpp Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,5 @@ +#include + +extern "C" void cpp_function(char const *name) { + std::cout << "Hello, " << name << "! I am Cpp!\n"; +} diff -r 2b4f361e3891 -r 629d5123a979 tools/corrosion/test/cpp2rust/cpp2rust/lib2.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/corrosion/test/cpp2rust/cpp2rust/lib2.cpp Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,11 @@ +#include +#include + +extern "C" void cpp_function2(char const *name) { + std::cout << "Hello, " << name << "! I am Cpp library Number 2!\n"; +} + +extern "C" uint32_t get_42() { + uint32_t v = 42; + return v; +} diff -r 2b4f361e3891 -r 629d5123a979 tools/corrosion/test/cpp2rust/cpp2rust/path with space/lib3.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/corrosion/test/cpp2rust/cpp2rust/path with space/lib3.cpp Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,8 @@ +// Check that libraries located at a path containing a space can also be linked. + +#include + +extern "C" void cpp_function3(char const *name) { + std::cout << "Hello, " << name << "! I am Cpp library Number 3!\n"; +} + diff -r 2b4f361e3891 -r 629d5123a979 tools/corrosion/test/cpp2rust/cpp2rust/rust/Cargo.toml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/corrosion/test/cpp2rust/cpp2rust/rust/Cargo.toml Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,9 @@ +[package] +name = "rust-exe" +version = "0.1.0" +authors = ["Andrew Gaspar "] +license = "MIT" +edition = "2018" + +[dependencies] +rust-dependency = { path = "rust_dependency" } diff -r 2b4f361e3891 -r 629d5123a979 tools/corrosion/test/cpp2rust/cpp2rust/rust/build.rs --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/corrosion/test/cpp2rust/cpp2rust/rust/build.rs Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,4 @@ +// Build-scripts also need to be linked, so just add a dummy buildscript ensuring this works. +fn main() { + println!("Build-script is running.") +} diff -r 2b4f361e3891 -r 629d5123a979 tools/corrosion/test/cpp2rust/cpp2rust/rust/rust_dependency/Cargo.toml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/corrosion/test/cpp2rust/cpp2rust/rust/rust_dependency/Cargo.toml Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,8 @@ +[package] +name = "rust-dependency" +version = "0.1.0" +license = "MIT" +edition = "2018" + +[dependencies] + diff -r 2b4f361e3891 -r 629d5123a979 tools/corrosion/test/cpp2rust/cpp2rust/rust/rust_dependency/src/lib.rs --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/corrosion/test/cpp2rust/cpp2rust/rust/rust_dependency/src/lib.rs Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,8 @@ + +extern "C" { + fn get_42() -> u32; +} +pub fn calls_ffi() { + let res = unsafe { get_42()}; + assert_eq!(res, 42); +} diff -r 2b4f361e3891 -r 629d5123a979 tools/corrosion/test/crate_type/CMakeLists.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/corrosion/test/crate_type/CMakeLists.txt Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,6 @@ +corrosion_tests_add_test(crate_type "cpp-exe") + + +set_tests_properties("crate_type_run_cpp-exe" PROPERTIES PASS_REGULAR_EXPRESSION + "Hello from lib 1!\r?\nHello from lib 2!" + ) diff -r 2b4f361e3891 -r 629d5123a979 tools/corrosion/test/crate_type/crate_type/CMakeLists.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/corrosion/test/crate_type/crate_type/CMakeLists.txt Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,11 @@ +cmake_minimum_required(VERSION 3.15) +project(test_project VERSION 0.1.0) +include(../../test_header.cmake) + +# Add --crate-type to ensure that only the specified type of library is built and no error is thrown +corrosion_import_crate(MANIFEST_PATH proj1/Cargo.toml CRATE_TYPES staticlib FLAGS --crate-type=staticlib) +corrosion_import_crate(MANIFEST_PATH proj2/Cargo.toml CRATE_TYPES cdylib FLAGS --crate-type=cdylib) + +add_executable(cpp-exe main.cpp) +target_link_libraries(cpp-exe proj1) +target_link_libraries(cpp-exe proj2) diff -r 2b4f361e3891 -r 629d5123a979 tools/corrosion/test/crate_type/crate_type/main.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/corrosion/test/crate_type/crate_type/main.cpp Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,8 @@ +extern "C" void rust_function1(); +extern "C" void rust_function2(); + +int main() { + rust_function1(); + rust_function2(); + return 0; +} diff -r 2b4f361e3891 -r 629d5123a979 tools/corrosion/test/crate_type/crate_type/proj1/Cargo.toml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/corrosion/test/crate_type/crate_type/proj1/Cargo.toml Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,10 @@ +[package] +name = "proj1" +version = "0.1.0" +edition = "2018" + +[dependencies] + +[lib] +crate-type=["staticlib", "cdylib"] + diff -r 2b4f361e3891 -r 629d5123a979 tools/corrosion/test/crate_type/crate_type/proj1/src/lib.rs --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/corrosion/test/crate_type/crate_type/proj1/src/lib.rs Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,4 @@ +#[no_mangle] +pub extern "C" fn rust_function1() { + println!("Hello from lib 1!"); +} diff -r 2b4f361e3891 -r 629d5123a979 tools/corrosion/test/crate_type/crate_type/proj2/Cargo.toml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/corrosion/test/crate_type/crate_type/proj2/Cargo.toml Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,9 @@ +[package] +name = "proj2" +version = "0.1.0" +edition = "2018" + +[dependencies] + +[lib] +crate-type=["staticlib", "cdylib"] diff -r 2b4f361e3891 -r 629d5123a979 tools/corrosion/test/crate_type/crate_type/proj2/src/lib.rs --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/corrosion/test/crate_type/crate_type/proj2/src/lib.rs Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,4 @@ +#[no_mangle] +pub extern "C" fn rust_function2() { + println!("Hello from lib 2!"); +} diff -r 2b4f361e3891 -r 629d5123a979 tools/corrosion/test/custom_profiles/CMakeLists.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/corrosion/test/custom_profiles/CMakeLists.txt Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,27 @@ +# The tests in this folder test specifying the cargo profile name via the --profile option. +# The built-in `test` and `bench` profiles are _not_ supported, because they output +# artifacts to a different location and add a hash to the artifact name. +if(Rust_VERSION VERSION_GREATER_EQUAL 1.57.0) + + corrosion_tests_add_test(custom_profiles_global "custom-profile-exe" TEST_SRC_DIR custom_profiles) + corrosion_tests_add_test(custom_profiles_target_specific "custom-profile-exe" + TEST_SRC_DIR custom_profiles + PASS_THROUGH_ARGS -DCORROSION_TEST_USE_TARGET_SPECIFIC_OVERRIDE=ON + ) + corrosion_tests_add_test(dev_profile "dev_bin" TEST_SRC_DIR basic_profiles CARGO_PROFILE dev) + corrosion_tests_add_test(release_profile "release_bin" TEST_SRC_DIR basic_profiles CARGO_PROFILE release) + + set_tests_properties("custom_profiles_global_run_custom-profile-exe" PROPERTIES PASS_REGULAR_EXPRESSION + "^Hello, Cpp! I'm Rust!\r?\n$" + ) + set_tests_properties("custom_profiles_target_specific_run_custom-profile-exe" PROPERTIES PASS_REGULAR_EXPRESSION + "^Hello, Cpp! I'm Rust!\r?\n$" + ) + set_tests_properties("dev_profile_run_dev_bin" PROPERTIES PASS_REGULAR_EXPRESSION + "^Hello, Cpp! I'm Rust!\r?\n$" + ) + set_tests_properties("release_profile_run_release_bin" PROPERTIES PASS_REGULAR_EXPRESSION + "^Hello, Cpp! I'm Rust!\r?\n$" + ) + +endif() diff -r 2b4f361e3891 -r 629d5123a979 tools/corrosion/test/custom_profiles/basic_profiles/CMakeLists.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/corrosion/test/custom_profiles/basic_profiles/CMakeLists.txt Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,12 @@ +cmake_minimum_required(VERSION 3.15) +project(test_project VERSION 0.1.0) +include(../../test_header.cmake) + +if(NOT DEFINED CARGO_PROFILE) + message(FATAL_ERROR "Test internal error. The test should be called with the CARGO_PROFILE parameter.") +endif() + +corrosion_import_crate(MANIFEST_PATH rust/Cargo.toml PROFILE ${CARGO_PROFILE}) + +add_executable(${CARGO_PROFILE}_bin main.cpp) +target_link_libraries(${CARGO_PROFILE}_bin PUBLIC cargo_profiles_lib) diff -r 2b4f361e3891 -r 629d5123a979 tools/corrosion/test/custom_profiles/basic_profiles/main.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/corrosion/test/custom_profiles/basic_profiles/main.cpp Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,6 @@ +extern "C" void rust_function(char const *name); + + +int main(int argc, char **argv) { + rust_function("Cpp"); +} diff -r 2b4f361e3891 -r 629d5123a979 tools/corrosion/test/custom_profiles/basic_profiles/rust/Cargo.toml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/corrosion/test/custom_profiles/basic_profiles/rust/Cargo.toml Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,7 @@ +[package] +name = "cargo-profiles-lib" +version = "0.1.0" +edition = "2021" + +[lib] +crate-type=["staticlib"] diff -r 2b4f361e3891 -r 629d5123a979 tools/corrosion/test/custom_profiles/basic_profiles/rust/src/lib.rs --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/corrosion/test/custom_profiles/basic_profiles/rust/src/lib.rs Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,8 @@ +use std::os::raw::c_char; + +#[no_mangle] +pub extern "C" fn rust_function(name: *const c_char) { + let name = unsafe { std::ffi::CStr::from_ptr(name).to_str().unwrap() }; + println!("Hello, {}! I'm Rust!", name); +} + diff -r 2b4f361e3891 -r 629d5123a979 tools/corrosion/test/custom_profiles/custom_profiles/CMakeLists.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/corrosion/test/custom_profiles/custom_profiles/CMakeLists.txt Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,20 @@ +cmake_minimum_required(VERSION 3.15) +project(test_project VERSION 0.1.0) +include(../../test_header.cmake) + +set(_release_profile $,release-without-dbg,custom-without-dbg>) +set(custom_profile $,dev-without-dbg,${_release_profile}>) + +if(CORROSION_TEST_USE_TARGET_SPECIFIC_OVERRIDE) + # Select "wrong" profile here on purpose. + corrosion_import_crate(MANIFEST_PATH rust/Cargo.toml PROFILE dev) + set_target_properties(custom_profiles_lib + PROPERTIES + INTERFACE_CORROSION_CARGO_PROFILE "${custom_profile}" + ) +else() + corrosion_import_crate(MANIFEST_PATH rust/Cargo.toml PROFILE ${custom_profile}) +endif() + +add_executable(custom-profile-exe main.cpp) +target_link_libraries(custom-profile-exe PUBLIC custom_profiles_lib) diff -r 2b4f361e3891 -r 629d5123a979 tools/corrosion/test/custom_profiles/custom_profiles/main.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/corrosion/test/custom_profiles/custom_profiles/main.cpp Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,6 @@ +extern "C" void rust_function(char const *name); + + +int main(int argc, char **argv) { + rust_function("Cpp"); +} diff -r 2b4f361e3891 -r 629d5123a979 tools/corrosion/test/custom_profiles/custom_profiles/rust/Cargo.toml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/corrosion/test/custom_profiles/custom_profiles/rust/Cargo.toml Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,24 @@ +[package] +name = "custom-profiles-lib" +version = "0.1.0" +edition = "2021" + +[lib] +crate-type=["staticlib"] + +# Test if neither release or debug where selected by only disabling debug-assertions in the inherited profile. +[profile.release] +debug-assertions = true + +[profile.dev-without-dbg] +inherits = "dev" +debug-assertions = false + +[profile.release-without-dbg] +inherits = "release" +debug-assertions = false + +[profile.custom-without-dbg] +inherits = "release" +opt-level = 1 +debug-assertions = false diff -r 2b4f361e3891 -r 629d5123a979 tools/corrosion/test/custom_profiles/custom_profiles/rust/src/lib.rs --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/corrosion/test/custom_profiles/custom_profiles/rust/src/lib.rs Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,11 @@ +use std::os::raw::c_char; + +#[no_mangle] +pub extern "C" fn rust_function(name: *const c_char) { + let name = unsafe { std::ffi::CStr::from_ptr(name).to_str().unwrap() }; + println!("Hello, {}! I'm Rust!", name); +} + + +#[cfg(debug_assertions)] +const _: () = assert!(false, "Debug assertions where not disabled via custom profile!"); diff -r 2b4f361e3891 -r 629d5123a979 tools/corrosion/test/cxxbridge/CMakeLists.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/corrosion/test/cxxbridge/CMakeLists.txt Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,20 @@ +if(CORROSION_TESTS_CXXBRIDGE) + corrosion_tests_add_test(cxxbridge_cpp2rust_1 "rust_bin" + TEST_SRC_DIR cxxbridge_cpp2rust + PASS_THROUGH_ARGS -DTEST_CXXBRIDGE_VARIANT1=ON + ) + corrosion_tests_add_test(cxxbridge_cpp2rust_2 "rust_bin" + TEST_SRC_DIR cxxbridge_cpp2rust + PASS_THROUGH_ARGS -DTEST_CXXBRIDGE_VARIANT2=ON + ) + corrosion_tests_add_test(cxxbridge_rust2cpp "cxxbridge-exe") + + set_tests_properties("cxxbridge_cpp2rust_1_run_rust_bin" + PROPERTIES PASS_REGULAR_EXPRESSION + "main function" + ) + set_tests_properties("cxxbridge_rust2cpp_run_cxxbridge-exe" + PROPERTIES PASS_REGULAR_EXPRESSION + "Hello cxxbridge from lib.rs! \\[4, 5, 6\\]\r?\nHello cxxbridge from foo/mod.rs! \\[4, 5, 6\\]" + ) +endif() diff -r 2b4f361e3891 -r 629d5123a979 tools/corrosion/test/cxxbridge/cxxbridge_cpp2rust/CMakeLists.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/corrosion/test/cxxbridge/cxxbridge_cpp2rust/CMakeLists.txt Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,39 @@ +cmake_minimum_required(VERSION 3.15) +project(test_project VERSION 0.1.0 LANGUAGES CXX) +include(../../test_header.cmake) +set(CMAKE_CXX_STANDARD 11) +set(CMAKE_CXX_STANDARD_REQUIRED 1) + +corrosion_import_crate(MANIFEST_PATH rust/Cargo.toml) +corrosion_add_cxxbridge(cxxbridge-cpp CRATE rust_bin FILES lib.rs) +target_include_directories(cxxbridge-cpp PRIVATE "include") + +if(CMAKE_SYSTEM_NAME STREQUAL "Linux" + OR (CMAKE_SYSTEM_NAME STREQUAL "Windows" + AND (CMAKE_CXX_COMPILER_ID STREQUAL "GNU" OR CMAKE_CXX_COMPILER_ID MATCHES "Clang") + ) +) + corrosion_add_target_local_rustflags(rust_bin "-Clink-arg=-fuse-ld=lld") +endif() + +if(MSVC) + set_target_properties(cxxbridge-cpp PROPERTIES MSVC_RUNTIME_LIBRARY "MultiThreadedDLL") +endif() + +if(TEST_CXXBRIDGE_VARIANT1) + # Variant 1: Merge the C++ User sources into the generated library target. + target_sources(cxxbridge-cpp PRIVATE cpplib.cpp) + corrosion_link_libraries(rust_bin cxxbridge-cpp) +elseif(TEST_CXXBRIDGE_VARIANT2) + # Variant 2: Create a separate C++ library and link both the User library and + # the generated library into rust + add_library(cpp_lib STATIC cpplib.cpp) + target_include_directories(cpp_lib PUBLIC "${CMAKE_CURRENT_LIST_DIR}/include") + target_link_libraries(cpp_lib PUBLIC cxxbridge-cpp) + corrosion_link_libraries(rust_bin cpp_lib cxxbridge-cpp) + if(MSVC) + set_target_properties(cpp_lib PROPERTIES MSVC_RUNTIME_LIBRARY "MultiThreadedDLL") + endif() +else() + message(FATAL_ERROR "Internal test error - required option not defined") +endif() diff -r 2b4f361e3891 -r 629d5123a979 tools/corrosion/test/cxxbridge/cxxbridge_cpp2rust/cpplib.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/corrosion/test/cxxbridge/cxxbridge_cpp2rust/cpplib.cpp Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,15 @@ +#include +#include "cpplib.h" +#include "cxxbridge-cpp/lib.h" +#include "rust/cxx.h" + +RsImage read_image(rust::Str path) { + std::cout << "read_image called" << std::endl; + std::cout << path << std::endl; + Rgba c = { 1.0, 2.0, 3.0, 4.0}; + RsImage v = { 1, 1, c}; + return v; +} +void write_image(::rust::Str path, ::RsImage const & image) { + +} diff -r 2b4f361e3891 -r 629d5123a979 tools/corrosion/test/cxxbridge/cxxbridge_cpp2rust/include/cpplib.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/corrosion/test/cxxbridge/cxxbridge_cpp2rust/include/cpplib.h Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,5 @@ +#pragma once +#include "cxxbridge-cpp/lib.h" + +::RsImage read_image(::rust::Str path); +void write_image(::rust::Str path, ::RsImage const & image); diff -r 2b4f361e3891 -r 629d5123a979 tools/corrosion/test/cxxbridge/cxxbridge_cpp2rust/rust/Cargo.toml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/corrosion/test/cxxbridge/cxxbridge_cpp2rust/rust/Cargo.toml Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,10 @@ +[package] +name = "rust_bin" +version = "0.1.0" +edition = "2018" + +[lib] +name = "cxxbridge_lib" + +[dependencies] +cxx = "1.0" diff -r 2b4f361e3891 -r 629d5123a979 tools/corrosion/test/cxxbridge/cxxbridge_cpp2rust/rust/src/lib.rs --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/corrosion/test/cxxbridge/cxxbridge_cpp2rust/rust/src/lib.rs Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,26 @@ +#[cxx::bridge] +pub mod ffi +{ + #[derive(Debug, PartialEq)] + pub struct Rgba + { + r: f32, + g: f32, + b: f32, + a: f32, + } + + #[derive(Debug,PartialEq)] + pub struct RsImage + { + width: usize, + height: usize, + raster: Rgba, + } + unsafe extern "C++" + { + include!("cpplib.h"); + pub fn read_image(path: &str) -> RsImage; + fn write_image(path: &str, image: &RsImage); + } +} \ No newline at end of file diff -r 2b4f361e3891 -r 629d5123a979 tools/corrosion/test/cxxbridge/cxxbridge_cpp2rust/rust/src/main.rs --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/corrosion/test/cxxbridge/cxxbridge_cpp2rust/rust/src/main.rs Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,14 @@ +use cxxbridge_lib::ffi::{RsImage,Rgba,read_image}; + +fn main() { + println!("main function"); + let expected = RsImage { width: 1, height: 1, raster: Rgba { + r: 1.0, + g: 2.0, + b: 3.0, + a: 4.0, + }}; + let actual = read_image("dummy path"); + println!("returned from C++"); + assert_eq!(actual, expected) +} \ No newline at end of file diff -r 2b4f361e3891 -r 629d5123a979 tools/corrosion/test/cxxbridge/cxxbridge_rust2cpp/CMakeLists.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/corrosion/test/cxxbridge/cxxbridge_rust2cpp/CMakeLists.txt Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,16 @@ +cmake_minimum_required(VERSION 3.15) +project(test_project VERSION 0.1.0) +include(../../test_header.cmake) +set(CMAKE_CXX_STANDARD 11) +set(CMAKE_CXX_STANDARD_REQUIRED 1) + +corrosion_import_crate(MANIFEST_PATH rust/Cargo.toml) +corrosion_add_cxxbridge(cxxbridge-cpp CRATE cxxbridge_crate MANIFEST_PATH rust FILES lib.rs foo/mod.rs) + +add_executable(cxxbridge-exe main.cpp) +target_link_libraries(cxxbridge-exe PUBLIC cxxbridge-cpp) + +if(MSVC) + # Note: This is required because we use `cxx` which uses `cc` to compile and link C++ code. + corrosion_set_env_vars(cxxbridge_crate "CFLAGS=-MDd" "CXXFLAGS=-MDd") +endif() diff -r 2b4f361e3891 -r 629d5123a979 tools/corrosion/test/cxxbridge/cxxbridge_rust2cpp/main.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/corrosion/test/cxxbridge/cxxbridge_rust2cpp/main.cpp Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,11 @@ +#include +#include +#include + +int main(int argc, char **argv) +{ + std::vector input = { 4, 5, 6}; + rust::Slice slice{input.data(), input.size()}; + lib::print(slice); + foo::print(slice); +} diff -r 2b4f361e3891 -r 629d5123a979 tools/corrosion/test/cxxbridge/cxxbridge_rust2cpp/rust/Cargo.toml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/corrosion/test/cxxbridge/cxxbridge_rust2cpp/rust/Cargo.toml Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,10 @@ +[package] +name = "cxxbridge-crate" +version = "0.1.0" +edition = "2018" + +[lib] +crate-type = ["staticlib"] + +[dependencies] +cxx = "1.0" diff -r 2b4f361e3891 -r 629d5123a979 tools/corrosion/test/cxxbridge/cxxbridge_rust2cpp/rust/src/foo/mod.rs --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/corrosion/test/cxxbridge/cxxbridge_rust2cpp/rust/src/foo/mod.rs Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,10 @@ +#[cxx::bridge(namespace = "foo")] +mod bridge { + extern "Rust" { + fn print(slice: &[u64]); + } +} + +fn print(slice: &[u64]) { + println!("Hello cxxbridge from foo/mod.rs! {:?}", slice); +} diff -r 2b4f361e3891 -r 629d5123a979 tools/corrosion/test/cxxbridge/cxxbridge_rust2cpp/rust/src/lib.rs --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/corrosion/test/cxxbridge/cxxbridge_rust2cpp/rust/src/lib.rs Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,12 @@ +mod foo; + +#[cxx::bridge(namespace = "lib")] +mod bridge { + extern "Rust" { + fn print(slice: &[u64]); + } +} + +fn print(slice: &[u64]) { + println!("Hello cxxbridge from lib.rs! {:?}", slice); +} diff -r 2b4f361e3891 -r 629d5123a979 tools/corrosion/test/envvar/CMakeLists.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/corrosion/test/envvar/CMakeLists.txt Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,5 @@ +corrosion_tests_add_test(envvar "program_requiring_rust_lib_with_envvar") + +set_tests_properties("envvar_run_program_requiring_rust_lib_with_envvar" PROPERTIES PASS_REGULAR_EXPRESSION + "Ok" + ) diff -r 2b4f361e3891 -r 629d5123a979 tools/corrosion/test/envvar/envvar/.cargo/config.toml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/corrosion/test/envvar/envvar/.cargo/config.toml Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,2 @@ +[env] +COR_CONFIG_TOML_ENV_VAR = "EnvVariableSetViaConfig.toml" diff -r 2b4f361e3891 -r 629d5123a979 tools/corrosion/test/envvar/envvar/CMakeLists.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/corrosion/test/envvar/envvar/CMakeLists.txt Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,23 @@ +cmake_minimum_required(VERSION 3.15) +project(test_project VERSION 0.1.0) +include(../../test_header.cmake) + +corrosion_import_crate(MANIFEST_PATH Cargo.toml) + +corrosion_set_env_vars(rust_lib_requiring_envvar + "ANOTHER_VARIABLE=ANOTHER_VALUE" + "$" + "COR_CARGO_VERSION_MAJOR=${Rust_CARGO_VERSION_MAJOR}" + "COR_CARGO_VERSION_MINOR=${Rust_CARGO_VERSION_MINOR}" +) + +add_executable(program_requiring_rust_lib_with_envvar main.cpp) + +set_property( + TARGET program_requiring_rust_lib_with_envvar + APPEND + PROPERTY INDIRECT_VAR_TEST + "REQUIRED_VARIABLE=EXPECTED_VALUE" +) + +target_link_libraries(program_requiring_rust_lib_with_envvar PUBLIC rust_lib_requiring_envvar) diff -r 2b4f361e3891 -r 629d5123a979 tools/corrosion/test/envvar/envvar/Cargo.toml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/corrosion/test/envvar/envvar/Cargo.toml Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,9 @@ +[package] +name = "rust-lib-requiring-envvar" +version = "0.1.0" +authors = ["Olivier Goffart "] +edition = "2018" +build = "build.rs" + +[lib] +crate-type = [ "lib", "cdylib" ] diff -r 2b4f361e3891 -r 629d5123a979 tools/corrosion/test/envvar/envvar/build.rs --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/corrosion/test/envvar/envvar/build.rs Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,18 @@ +fn main() { + assert_eq!(env!("REQUIRED_VARIABLE"), "EXPECTED_VALUE"); + assert_eq!(std::env::var("ANOTHER_VARIABLE").unwrap(), "ANOTHER_VALUE"); + let cargo_major = env!("COR_CARGO_VERSION_MAJOR") + .parse::() + .expect("Invalid Major version"); + let cargo_minor = env!("COR_CARGO_VERSION_MINOR") + .parse::() + .expect("Invalid Minor version"); + + // The `[env]` section in `.cargo/config.toml` was added in version 1.56. + if cargo_major > 1 || (cargo_major == 1 && cargo_minor >= 56) { + // Check if cargo picks up the config.toml, which sets this additional env variable. + let env_value = option_env!("COR_CONFIG_TOML_ENV_VAR") + .expect("Test failure! Cargo >= 1.56.0 should set this environment variable"); + assert_eq!(env_value, "EnvVariableSetViaConfig.toml"); + } +} diff -r 2b4f361e3891 -r 629d5123a979 tools/corrosion/test/envvar/envvar/main.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/corrosion/test/envvar/envvar/main.cpp Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,5 @@ +#include + +int main() { + std::cout << "Ok"; +} diff -r 2b4f361e3891 -r 629d5123a979 tools/corrosion/test/envvar/envvar/src/lib.rs --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/corrosion/test/envvar/envvar/src/lib.rs Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,7 @@ +#[cfg(test)] +mod tests { + #[test] + fn it_works() { + assert_eq!(2 + 2, 4); + } +} diff -r 2b4f361e3891 -r 629d5123a979 tools/corrosion/test/features/CMakeLists.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/corrosion/test/features/CMakeLists.txt Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,5 @@ +corrosion_tests_add_test(features "features-cpp-exe") + +set_tests_properties("features_run_features-cpp-exe" PROPERTIES PASS_REGULAR_EXPRESSION + "Hello, Cpp! I'm Rust!\r?\nHello, Cpp again! I'm Rust again!\r?\nHello, Cpp again! I'm Rust again, third time the charm!" + ) diff -r 2b4f361e3891 -r 629d5123a979 tools/corrosion/test/features/features/CMakeLists.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/corrosion/test/features/features/CMakeLists.txt Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,27 @@ +cmake_minimum_required(VERSION 3.15) +project(test_project VERSION 0.1.0) +include(../../test_header.cmake) + +corrosion_import_crate(MANIFEST_PATH rust/Cargo.toml FEATURES thirdfeature ALL_FEATURES) + +add_executable(features-cpp-exe main.cpp) +target_link_libraries(features-cpp-exe PUBLIC rust_feature_lib) + +corrosion_set_features(rust_feature_lib + ALL_FEATURES OFF + NO_DEFAULT_FEATURES + FEATURES + $ +) + +set_property( + TARGET features-cpp-exe + APPEND + PROPERTY app_features myfeature +) +set_property( + TARGET features-cpp-exe + APPEND + PROPERTY app_features secondfeature +) + diff -r 2b4f361e3891 -r 629d5123a979 tools/corrosion/test/features/features/main.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/corrosion/test/features/features/main.cpp Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,13 @@ +extern "C" void rust_function(char const *name); +extern "C" void rust_second_function(char const *name); +extern "C" void rust_third_function(char const *name); + +int main(int argc, char **argv) { + if (argc < 2) { + rust_function("Cpp"); + rust_second_function("Cpp again"); + rust_third_function("Cpp again"); + } else { + rust_function(argv[1]); + } +} diff -r 2b4f361e3891 -r 629d5123a979 tools/corrosion/test/features/features/rust/Cargo.toml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/corrosion/test/features/features/rust/Cargo.toml Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,18 @@ +[package] +name = "rust-feature-lib" +version = "0.1.0" +authors = ["Andrew Gaspar "] +license = "MIT" +edition = "2018" + +[dependencies] + +[lib] +crate-type=["staticlib"] + +[features] +default = ["compile-breakage"] +myfeature = [] +secondfeature = [] +thirdfeature = [] +compile-breakage = [] \ No newline at end of file diff -r 2b4f361e3891 -r 629d5123a979 tools/corrosion/test/features/features/rust/src/lib.rs --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/corrosion/test/features/features/rust/src/lib.rs Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,26 @@ +#[cfg(feature = "myfeature")] +use std::os::raw::c_char; + +#[no_mangle] +#[cfg(feature = "myfeature")] +pub extern "C" fn rust_function(name: *const c_char) { + let name = unsafe { std::ffi::CStr::from_ptr(name).to_str().unwrap() }; + println!("Hello, {}! I'm Rust!", name); +} + +#[no_mangle] +#[cfg(feature = "secondfeature")] +pub extern "C" fn rust_second_function(name: *const c_char) { + let name = unsafe { std::ffi::CStr::from_ptr(name).to_str().unwrap() }; + println!("Hello, {}! I'm Rust again!", name); +} + +#[no_mangle] +#[cfg(feature = "thirdfeature")] +pub extern "C" fn rust_third_function(name: *const c_char) { + let name = unsafe { std::ffi::CStr::from_ptr(name).to_str().unwrap() }; + println!("Hello, {}! I'm Rust again, third time the charm!", name); +} + +#[cfg(feature = "compile-breakage")] +const _: [(); 1] = [(); 2]; // Trigger a compile error to make sure that we succeeded in de-activating this feature diff -r 2b4f361e3891 -r 629d5123a979 tools/corrosion/test/find_rust/CMakeLists.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/corrosion/test/find_rust/CMakeLists.txt Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,3 @@ +corrosion_tests_add_test(find_rust "") +corrosion_tests_add_test(rustup_proxy "") + diff -r 2b4f361e3891 -r 629d5123a979 tools/corrosion/test/find_rust/find_rust/CMakeLists.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/corrosion/test/find_rust/find_rust/CMakeLists.txt Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,9 @@ + +cmake_minimum_required(VERSION 3.15) +project(FindRust LANGUAGES CXX) + +set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/../../../cmake" ${CMAKE_MODULE_PATH}) + +# make sure find_package(Rust) can be used more than once +find_package(Rust REQUIRED) +find_package(Rust REQUIRED) diff -r 2b4f361e3891 -r 629d5123a979 tools/corrosion/test/find_rust/rustup_proxy/CMakeLists.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/corrosion/test/find_rust/rustup_proxy/CMakeLists.txt Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,47 @@ +cmake_minimum_required(VERSION 3.15) +project(RustupProxy LANGUAGES CXX) + +set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/../../../cmake" ${CMAKE_MODULE_PATH}) + +function(_assert_is_rustup_proxy executable_path) + execute_process( + COMMAND + ${CMAKE_COMMAND} -E env + RUSTUP_FORCE_ARG0=rustup + "${executable_path}" --version + OUTPUT_VARIABLE _VERSION_RAW + ERROR_VARIABLE _VERSION_STDERR + RESULT_VARIABLE _VERSION_RESULT + ) + + if(NOT _VERSION_RESULT EQUAL "0") + message(FATAL_ERROR "`${executable_path} --version` failed with ${_VERSION_RESULT}\n" + "stderr:\n${_VERSION_STDERR}" + ) + endif() + + if (NOT _VERSION_RAW MATCHES "rustup [0-9\\.]+") + message(FATAL_ERROR "`${executable_path} --version` output does not match rustup: ${_VERSION_RAW}\n") + endif() +endfunction() + +set(Rust_RESOLVE_RUSTUP_TOOLCHAINS OFF CACHE BOOL "" FORCE) +find_package(Rust REQUIRED) + +if (NOT Rust_FOUND) + message(FATAL_ERROR "Rustup not found") +endif() + +get_property( + RUSTC_EXECUTABLE + TARGET Rust::Rustc PROPERTY IMPORTED_LOCATION +) + +_assert_is_rustup_proxy(${RUSTC_EXECUTABLE}) + +get_property( + CARGO_EXECUTABLE + TARGET Rust::Cargo PROPERTY IMPORTED_LOCATION +) + +_assert_is_rustup_proxy(${CARGO_EXECUTABLE}) diff -r 2b4f361e3891 -r 629d5123a979 tools/corrosion/test/gensource/CMakeLists.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/corrosion/test/gensource/CMakeLists.txt Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,5 @@ +corrosion_tests_add_test(gensource "") + +#set_tests_properties("features_run_features-cpp-exe" PROPERTIES PASS_REGULAR_EXPRESSION +# "Hello, Cpp! I'm Rust!\r?\nHello, Cpp again! I'm Rust again!\r?\nHello, Cpp again! I'm Rust again, third time the charm!" +# ) \ No newline at end of file diff -r 2b4f361e3891 -r 629d5123a979 tools/corrosion/test/gensource/gensource/.gitignore --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/corrosion/test/gensource/gensource/.gitignore Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,1 @@ +src/foo.rs diff -r 2b4f361e3891 -r 629d5123a979 tools/corrosion/test/gensource/gensource/CMakeLists.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/corrosion/test/gensource/gensource/CMakeLists.txt Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,23 @@ +cmake_minimum_required(VERSION 3.15) +project(test_project VERSION 0.1.0) +include(../../test_header.cmake) + +add_subdirectory(generator) + +add_custom_command( + OUTPUT "${CMAKE_CURRENT_SOURCE_DIR}/src/foo.rs" + DEPENDS $ + COMMAND $ "${CMAKE_CURRENT_SOURCE_DIR}/src/foo.rs" +) + +add_custom_target(after_generation DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/src/foo.rs") +add_custom_target(genexdebug COMMAND ${CMAKE_COMMAND} -E echo "Config DEBUG: $ Config Release: $ IMPORTED_LOCATION: $") + +corrosion_import_crate(MANIFEST_PATH ${CMAKE_CURRENT_SOURCE_DIR}/Cargo.toml) +add_dependencies(cargo-prebuild_generated after_generation) + +# Simple test for corrosion_parse_package_version +corrosion_parse_package_version("${CMAKE_CURRENT_SOURCE_DIR}/Cargo.toml" srcgen_version) +if (NOT "${srcgen_version}" VERSION_EQUAL "0.1.0") + message(FATAL_ERROR "Test failed to parse expected version") +endif() diff -r 2b4f361e3891 -r 629d5123a979 tools/corrosion/test/gensource/gensource/Cargo.toml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/corrosion/test/gensource/gensource/Cargo.toml Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,10 @@ +[package] +name = "generated" +version = "0.1.0" +edition = "2018" + +[lib] +crate-type = ["lib", "cdylib"] +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff -r 2b4f361e3891 -r 629d5123a979 tools/corrosion/test/gensource/gensource/generator/CMakeLists.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/corrosion/test/gensource/gensource/generator/CMakeLists.txt Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,2 @@ +corrosion_import_crate(MANIFEST_PATH Cargo.toml) +corrosion_set_hostbuild(srcgen) diff -r 2b4f361e3891 -r 629d5123a979 tools/corrosion/test/gensource/gensource/generator/Cargo.toml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/corrosion/test/gensource/gensource/generator/Cargo.toml Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,8 @@ +[package] +name = "srcgen" +version = "0.1.0" +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff -r 2b4f361e3891 -r 629d5123a979 tools/corrosion/test/gensource/gensource/generator/src/main.rs --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/corrosion/test/gensource/gensource/generator/src/main.rs Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,6 @@ +use std::io::Write; +fn main() -> Result<(), std::io::Error> { + let out_name = std::env::args().skip(1).next().unwrap(); + let mut out_file = std::fs::File::create(out_name)?; + Ok(write!(out_file, "const _: () = ();")?) +} diff -r 2b4f361e3891 -r 629d5123a979 tools/corrosion/test/gensource/gensource/src/lib.rs --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/corrosion/test/gensource/gensource/src/lib.rs Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,10 @@ +mod foo; + +#[cfg(test)] +mod tests { + #[test] + fn it_works() { + let result = 2 + 2; + assert_eq!(result, 4); + } +} diff -r 2b4f361e3891 -r 629d5123a979 tools/corrosion/test/hostbuild/CMakeLists.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/corrosion/test/hostbuild/CMakeLists.txt Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,6 @@ +corrosion_tests_add_test(hostbuild "rust-host-program" IS_HOSTBUILD) + +set_tests_properties("hostbuild_run_rust-host-program" PROPERTIES PASS_REGULAR_EXPRESSION + "^ok\r?\nHello Rust Hostbuild, I am an external C function" + ) + diff -r 2b4f361e3891 -r 629d5123a979 tools/corrosion/test/hostbuild/hostbuild/CMakeLists.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/corrosion/test/hostbuild/hostbuild/CMakeLists.txt Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,7 @@ +cmake_minimum_required(VERSION 3.15) +project(test_project VERSION 0.1.0) +include(../../test_header.cmake) + +corrosion_import_crate(MANIFEST_PATH ${CMAKE_CURRENT_SOURCE_DIR}/Cargo.toml) + +corrosion_set_hostbuild(rust-host-program) diff -r 2b4f361e3891 -r 629d5123a979 tools/corrosion/test/hostbuild/hostbuild/Cargo.toml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/corrosion/test/hostbuild/hostbuild/Cargo.toml Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,8 @@ +[package] +name = "rust-host-program" +version = "0.1.0" +authors = ["Olivier Goffart "] +edition = "2018" + +[build-dependencies] +cc = "1.0" diff -r 2b4f361e3891 -r 629d5123a979 tools/corrosion/test/hostbuild/hostbuild/build.rs --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/corrosion/test/hostbuild/hostbuild/build.rs Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,10 @@ +fn main() { + let out_dir = std::env::var("OUT_DIR").unwrap(); + cc::Build::new() + .file("src/lib.c") + .compile("hello"); + + println!("cargo:rustc-link-search=native={}", out_dir); + println!("cargo:rustc-link-lib=hello"); + println!("cargo:rerun-if-changed=src/lib.c"); +} \ No newline at end of file diff -r 2b4f361e3891 -r 629d5123a979 tools/corrosion/test/hostbuild/hostbuild/src/lib.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/corrosion/test/hostbuild/hostbuild/src/lib.c Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,5 @@ +#include + +void c_function(char const *name) { + printf("Hello %s, I am an external C function\n", name); +} diff -r 2b4f361e3891 -r 629d5123a979 tools/corrosion/test/hostbuild/hostbuild/src/main.rs --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/corrosion/test/hostbuild/hostbuild/src/main.rs Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,13 @@ +use std::os::raw::c_char; + +extern "C" { + fn c_function(name: *const c_char); +} + +fn main() { + println!("ok"); + let name = b"Rust Hostbuild\0"; + unsafe { + c_function(name.as_ptr() as _); + } +} diff -r 2b4f361e3891 -r 629d5123a979 tools/corrosion/test/multitarget/CMakeLists.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/corrosion/test/multitarget/CMakeLists.txt Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,22 @@ +corrosion_tests_add_test(multitarget "bin1;bin2;bin3") + +# Don't run this test in parallel with others, since the target directory size may cause issues. +set_tests_properties("multitarget_build" PROPERTIES RUN_SERIAL TRUE) + +set_tests_properties("multitarget_run_bin1" PROPERTIES PASS_REGULAR_EXPRESSION + "Hello, world!\r?\nHello, bin1! I'm Cpp!" + RUN_SERIAL + TRUE + ) + +set_tests_properties("multitarget_run_bin2" PROPERTIES PASS_REGULAR_EXPRESSION + "Hello, world!\r?\nHello, bin2! I'm Cpp!" + RUN_SERIAL + TRUE + ) + +set_tests_properties("multitarget_run_bin3" PROPERTIES PASS_REGULAR_EXPRESSION + "Hello, world!\r?\nHello, bin3! I'm Cpp!" + RUN_SERIAL + TRUE + ) diff -r 2b4f361e3891 -r 629d5123a979 tools/corrosion/test/multitarget/multitarget/CMakeLists.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/corrosion/test/multitarget/multitarget/CMakeLists.txt Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,12 @@ +cmake_minimum_required(VERSION 3.15) +project(test_project VERSION 0.1.0) +include(../../test_header.cmake) + +corrosion_import_crate(MANIFEST_PATH Cargo.toml) + +add_library(cpp-lib4 lib.cpp) +target_compile_features(cpp-lib4 PRIVATE cxx_std_14) +set_property(TARGET cpp-lib4 PROPERTY POSITION_INDEPENDENT_CODE ON) +corrosion_link_libraries(bin1 cpp-lib4) +corrosion_link_libraries(bin2 cpp-lib4) +corrosion_link_libraries(bin3 cpp-lib4) diff -r 2b4f361e3891 -r 629d5123a979 tools/corrosion/test/multitarget/multitarget/Cargo.toml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/corrosion/test/multitarget/multitarget/Cargo.toml Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,19 @@ +[package] +name = "multitarget-crate" +version = "0.1.0" +edition = "2018" + +[dependencies] + +[lib] +name = "multitarget_lib" +crate-type=["lib", "staticlib", "cdylib"] + +[[bin]] +name = "bin1" + +[[bin]] +name = "bin2" + +[[bin]] +name = "bin3" diff -r 2b4f361e3891 -r 629d5123a979 tools/corrosion/test/multitarget/multitarget/lib.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/corrosion/test/multitarget/multitarget/lib.cpp Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,5 @@ +#include + +extern "C" void cpp_function(char const *name) { + std::cout << "Hello, " << name << "! I'm Cpp!\n"; +} diff -r 2b4f361e3891 -r 629d5123a979 tools/corrosion/test/multitarget/multitarget/src/lib.rs --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/corrosion/test/multitarget/multitarget/src/lib.rs Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,9 @@ +use std::os::raw::c_char; + +pub fn hello_world() { + println!("Hello, world!"); +} + +extern "C" { + pub fn cpp_function(name: *const c_char); +} diff -r 2b4f361e3891 -r 629d5123a979 tools/corrosion/test/nostd/CMakeLists.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/corrosion/test/nostd/CMakeLists.txt Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,1 @@ +corrosion_tests_add_test(nostd "") diff -r 2b4f361e3891 -r 629d5123a979 tools/corrosion/test/nostd/nostd/CMakeLists.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/corrosion/test/nostd/nostd/CMakeLists.txt Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,11 @@ +cmake_minimum_required(VERSION 3.15) +project(test_project VERSION 0.1.0) +include(../../test_header.cmake) + +corrosion_import_crate(MANIFEST_PATH rust/Cargo.toml NO_STD) + +set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -nostdlib") +list(REMOVE_ITEM CMAKE_CXX_IMPLICIT_LINK_LIBRARIES stdc++) + +add_library(nostd-cpp-lib STATIC main.cpp) +target_link_libraries(nostd-cpp-lib PUBLIC rust-nostd-lib) diff -r 2b4f361e3891 -r 629d5123a979 tools/corrosion/test/nostd/nostd/main.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/corrosion/test/nostd/nostd/main.cpp Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,6 @@ +extern "C" void rust_function(); + +extern "C" void cpp_function() { + // Fail on linking issues + rust_function(); +} \ No newline at end of file diff -r 2b4f361e3891 -r 629d5123a979 tools/corrosion/test/nostd/nostd/rust/Cargo.toml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/corrosion/test/nostd/nostd/rust/Cargo.toml Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,17 @@ +[package] +name = "rust-nostd-lib" +version = "0.1.0" +edition = "2015" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] + +[lib] +crate-type=["staticlib"] + +[profile.release] +panic = "abort" + +[profile.dev] +panic = "abort" diff -r 2b4f361e3891 -r 629d5123a979 tools/corrosion/test/nostd/nostd/rust/src/lib.rs --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/corrosion/test/nostd/nostd/rust/src/lib.rs Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,10 @@ +#![no_std] +use core::panic::PanicInfo; + +#[no_mangle] +pub extern "C" fn rust_function() {} + +#[panic_handler] +fn panic(_panic: &PanicInfo<'_>) -> ! { + loop {} +} \ No newline at end of file diff -r 2b4f361e3891 -r 629d5123a979 tools/corrosion/test/output directory/CMakeLists.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/corrosion/test/output directory/CMakeLists.txt Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,154 @@ +set(configure_cmake_args) +if(CMAKE_C_COMPILER) + list(APPEND configure_cmake_args "C_COMPILER" "${CMAKE_C_COMPILER}") +endif() +if(CMAKE_CXX_COMPILER) + list(APPEND configure_cmake_args "CXX_COMPILER" "${CMAKE_CXX_COMPILER}") +endif() +if(CMAKE_C_COMPILER_TARGET) + list(APPEND configure_cmake_args "C_COMPILER_TARGET" "${CMAKE_C_COMPILER_TARGET}") +endif() +if(CMAKE_CXX_COMPILER_TARGET) + list(APPEND configure_cmake_args "CXX_COMPILER_TARGET" "${CMAKE_CXX_COMPILER_TARGET}") +endif() +if(CMAKE_GENERATOR_PLATFORM) + list(APPEND configure_cmake_args "GENERATOR_PLATFORM" "${CMAKE_GENERATOR_PLATFORM}") +endif() +if(CMAKE_OSX_ARCHITECTURES) + list(APPEND configure_cmake_args OSX_ARCHITECTURES "${CMAKE_OSX_ARCHITECTURES}") +endif() +if(CMAKE_TOOLCHAIN_FILE) + list(APPEND configure_cmake_args TOOLCHAIN_FILE "${CMAKE_TOOLCHAIN_FILE}") +endif() + +add_test(NAME "output_directory_build" + COMMAND + ${CMAKE_COMMAND} + -P "${CMAKE_SOURCE_DIR}/test/ConfigureAndBuild.cmake" + SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/output directory" + BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}/build" + GENERATOR "${CMAKE_GENERATOR}" + RUST_TOOLCHAIN "${Rust_TOOLCHAIN}" + CARGO_TARGET "${Rust_CARGO_TARGET}" + SYSTEM_NAME "${CMAKE_SYSTEM_NAME}" + ${configure_cmake_args} + + COMMAND_EXPAND_LISTS +) +set_tests_properties("output_directory_build" PROPERTIES FIXTURES_SETUP "build_fixture_output_directory") +if(CORROSION_TESTS_INSTALL_CORROSION) + set_tests_properties("output_directory_build" PROPERTIES FIXTURES_REQUIRED "fixture_corrosion_install") +endif() + +foreach(output_approach targetprop var targetprop_pdb_fallback) + if(output_approach STREQUAL "targetprop") + set(rust_proj_suffix "1") + elseif(output_approach STREQUAL "var") + set(rust_proj_suffix "2") + elseif(output_approach STREQUAL "targetprop_pdb_fallback") + set(rust_proj_suffix "3") + else() + message(FATAL_ERROR "specify rust project suffix for new output approach ${output_approach}") + endif() + + set(bin_name "rust_bin${rust_proj_suffix}${CMAKE_EXECUTABLE_SUFFIX}") + + add_test(NAME output_directory_bin_${output_approach} + COMMAND + "${CMAKE_COMMAND}" + -P "${CMAKE_CURRENT_SOURCE_DIR}/TestFileExists.cmake" + "${CMAKE_CURRENT_BINARY_DIR}/build/custom_bin_${output_approach}/${bin_name}" + ) + set_tests_properties("output_directory_bin_${output_approach}" PROPERTIES FIXTURES_REQUIRED "build_fixture_output_directory") + + set(lib_name "rust_lib${rust_proj_suffix}") + + set(static_lib_name "${CMAKE_STATIC_LIBRARY_PREFIX}${lib_name}${CMAKE_STATIC_LIBRARY_SUFFIX}") + + add_test(NAME output_directory_staticlib_${output_approach} + COMMAND + "${CMAKE_COMMAND}" + -P "${CMAKE_CURRENT_SOURCE_DIR}/TestFileExists.cmake" + "${CMAKE_CURRENT_BINARY_DIR}/build/custom_archive_${output_approach}/${static_lib_name}" + ) + set_tests_properties("output_directory_staticlib_${output_approach}" PROPERTIES FIXTURES_REQUIRED "build_fixture_output_directory") + + if(MINGW) + # Windows-GNU defines "lib" as prefix for DLLs, but cargo creates foo.dll instead of libfoo.dll + set(dynamic_lib_prefix "") + else() + set(dynamic_lib_prefix "${CMAKE_SHARED_LIBRARY_PREFIX}") + endif() + set(dynamic_lib_name "${dynamic_lib_prefix}${lib_name}${CMAKE_SHARED_LIBRARY_SUFFIX}") + + add_test(NAME output_directory_cdylib_${output_approach} + COMMAND + "${CMAKE_COMMAND}" + -P "${CMAKE_CURRENT_SOURCE_DIR}/TestFileExists.cmake" + "${CMAKE_CURRENT_BINARY_DIR}/build/custom_lib_${output_approach}/${dynamic_lib_name}" + ) + set_tests_properties("output_directory_cdylib_${output_approach}" PROPERTIES FIXTURES_REQUIRED "build_fixture_output_directory") + + if(WIN32) + set(implib_name ${CMAKE_IMPORT_LIBRARY_PREFIX}${lib_name}${CMAKE_IMPORT_LIBRARY_SUFFIX}) + + add_test(NAME output_directory_implib_${output_approach} + COMMAND + "${CMAKE_COMMAND}" + -P "${CMAKE_CURRENT_SOURCE_DIR}/TestFileExists.cmake" + # Implib is an ARCHIVE artifact, see: + # https://cmake.org/cmake/help/v3.25/manual/cmake-buildsystem.7.html#archive-output-artifacts + "${CMAKE_CURRENT_BINARY_DIR}/build/custom_archive_${output_approach}/${implib_name}" + ) + set_tests_properties("output_directory_implib_${output_approach}" PROPERTIES FIXTURES_REQUIRED "build_fixture_output_directory") + + if(MSVC) + if(output_approach STREQUAL "targetprop") + set(expected_lib_pdb_path "custom_lib_pdb_targetprop") + set(expected_bin_pdb_path "custom_bin_pdb_targetprop") + elseif(output_approach STREQUAL "var") + # When using a CMAKE_ variable instead of a target property, both targets + # end up in the same directory. + set(expected_lib_pdb_path "custom_binlib_pdb_var") + set(expected_bin_pdb_path "custom_binlib_pdb_var") + elseif(output_approach STREQUAL "targetprop_pdb_fallback") + set(expected_lib_pdb_path "custom_lib_targetprop_pdb_fallback") + set(expected_bin_pdb_path "custom_bin_targetprop_pdb_fallback") + else() + message(FATAL_ERROR "specify rust project suffix for new output approach ${output_approach}") + endif() + + set(lib_pdb_name "${lib_name}.pdb") + add_test(NAME output_directory_cdylib_pdb_${output_approach} + COMMAND + "${CMAKE_COMMAND}" + -P "${CMAKE_CURRENT_SOURCE_DIR}/TestFileExists.cmake" + "${CMAKE_CURRENT_BINARY_DIR}/build/${expected_lib_pdb_path}/${lib_pdb_name}" + ) + set_tests_properties("output_directory_cdylib_pdb_${output_approach}" PROPERTIES FIXTURES_REQUIRED "build_fixture_output_directory") + + set(bin_pdb_name "rust_bin${rust_proj_suffix}.pdb") + add_test(NAME output_directory_bin_pdb_${output_approach} + COMMAND + "${CMAKE_COMMAND}" + -P "${CMAKE_CURRENT_SOURCE_DIR}/TestFileExists.cmake" + "${CMAKE_CURRENT_BINARY_DIR}/build/${expected_bin_pdb_path}/${bin_pdb_name}" + ) + set_tests_properties("output_directory_bin_pdb_${output_approach}" PROPERTIES FIXTURES_REQUIRED "build_fixture_output_directory") + endif() + endif() + +endforeach() + +add_test(NAME postbuild_custom_command + COMMAND + "${CMAKE_COMMAND}" + -P "${CMAKE_CURRENT_SOURCE_DIR}/TestFileExists.cmake" + "${CMAKE_CURRENT_BINARY_DIR}/build/another_dir/moved_bin" + ) +set_tests_properties("postbuild_custom_command" PROPERTIES FIXTURES_REQUIRED "build_fixture_output_directory") + +add_test(NAME "output_directory_cleanup" COMMAND "${CMAKE_COMMAND}" -E remove_directory "${CMAKE_CURRENT_BINARY_DIR}/build") +set_tests_properties("output_directory_cleanup" PROPERTIES FIXTURES_CLEANUP "build_fixture_output_directory") + + diff -r 2b4f361e3891 -r 629d5123a979 tools/corrosion/test/output directory/TestFileExists.cmake --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/corrosion/test/output directory/TestFileExists.cmake Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,13 @@ +# CMake script to test if a file exists. Errors if the file does not exist. +# Expect actual arguments to start at index 3 (cmake -P ) + +# Expect one argument +if(NOT (CMAKE_ARGC EQUAL "4")) + message(FATAL_ERROR "Test Internal Error: Unexpected ARGC Value: ${CMAKE_ARGC}.") +endif() + +set(FILE_PATH "${CMAKE_ARGV3}") + +if(NOT ( EXISTS "${FILE_PATH}" )) + message(FATAL_ERROR "Test failed: File `${FILE_PATH}` does not exist.") +endif() diff -r 2b4f361e3891 -r 629d5123a979 tools/corrosion/test/output directory/output directory/CMakeLists.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/corrosion/test/output directory/output directory/CMakeLists.txt Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,58 @@ +cmake_minimum_required(VERSION 3.15) +project(test_project VERSION 0.1.0) +include(../../test_header.cmake) + +corrosion_import_crate(MANIFEST_PATH proj1/Cargo.toml) + +# Note: The output directories defined here must be manually kept in sync with the expected test location. +set_target_properties(rust_bin1 + PROPERTIES + RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/custom_bin_targetprop" + PDB_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/custom_bin_pdb_targetprop" + +) +set_target_properties(rust_lib1 PROPERTIES ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/custom_archive_targetprop") +set_target_properties(rust_lib1 + PROPERTIES + LIBRARY_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/custom_lib_targetprop" + PDB_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/custom_lib_pdb_targetprop" +) + +add_custom_command(TARGET cargo-build_rust_bin1 POST_BUILD + COMMAND + ${CMAKE_COMMAND} -E make_directory "${CMAKE_CURRENT_BINARY_DIR}/another_dir" + COMMAND + ${CMAKE_COMMAND} -E copy_if_different "$" "${CMAKE_CURRENT_BINARY_DIR}/another_dir/moved_bin" + ) + +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/custom_bin_var") +set(CMAKE_PDB_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/custom_binlib_pdb_var") +set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/custom_archive_var") +set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/custom_lib_var") + +corrosion_import_crate(MANIFEST_PATH proj2/Cargo.toml) + +unset(CMAKE_RUNTIME_OUTPUT_DIRECTORY) +unset(CMAKE_PDB_OUTPUT_DIRECTORY) +unset(CMAKE_ARCHIVE_OUTPUT_DIRECTORY) +unset(CMAKE_LIBRARY_OUTPUT_DIRECTORY) +unset(CMAKE_PDB_OUTPUT_DIRECTORY) + +add_executable(consumer consumer.cpp) +add_dependencies(consumer cargo-build_rust_lib1 cargo-build_rust_lib2) + +target_link_libraries(consumer rust_lib1 rust_lib2) + + +corrosion_import_crate(MANIFEST_PATH proj3/Cargo.toml) + +# Note: The output directories defined here must be manually kept in sync with the expected test location. +set_target_properties(rust_bin3 + PROPERTIES + RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/custom_bin_targetprop_pdb_fallback" +) +set_target_properties(rust_lib3 + PROPERTIES + ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/custom_archive_targetprop_pdb_fallback" + LIBRARY_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/custom_lib_targetprop_pdb_fallback" +) diff -r 2b4f361e3891 -r 629d5123a979 tools/corrosion/test/output directory/output directory/consumer.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/corrosion/test/output directory/output directory/consumer.cpp Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,16 @@ +#include +#include + +extern "C" unsigned int ret_12(); + + +int main(int argc, char *argv[]) +{ + std::cout << "HI\n"; + unsigned int a = ret_12(); + if (a != 12) { + return -1; + } + + return 0; +} diff -r 2b4f361e3891 -r 629d5123a979 tools/corrosion/test/output directory/output directory/proj1/Cargo.toml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/corrosion/test/output directory/output directory/proj1/Cargo.toml Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,11 @@ +[package] +name = "rust_package1" +version = "0.1.0" +edition = "2018" + +[lib] +name = "rust_lib1" +crate-type=["staticlib", "cdylib"] + +[[bin]] +name = "rust_bin1" diff -r 2b4f361e3891 -r 629d5123a979 tools/corrosion/test/output directory/output directory/proj1/src/lib.rs --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/corrosion/test/output directory/output directory/proj1/src/lib.rs Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,4 @@ +#[no_mangle] +pub extern "C" fn ret_12() -> u32 { + 12 +} diff -r 2b4f361e3891 -r 629d5123a979 tools/corrosion/test/output directory/output directory/proj2/Cargo.toml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/corrosion/test/output directory/output directory/proj2/Cargo.toml Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,11 @@ +[package] +name = "rust_package2" +version = "0.1.0" +edition = "2018" + +[lib] +name = "rust_lib2" +crate-type=["staticlib", "cdylib"] + +[[bin]] +name = "rust_bin2" diff -r 2b4f361e3891 -r 629d5123a979 tools/corrosion/test/output directory/output directory/proj2/src/lib.rs --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/corrosion/test/output directory/output directory/proj2/src/lib.rs Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,4 @@ +#[no_mangle] +pub extern "C" fn ret_12() -> u32 { + 12 +} diff -r 2b4f361e3891 -r 629d5123a979 tools/corrosion/test/output directory/output directory/proj3/Cargo.toml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/corrosion/test/output directory/output directory/proj3/Cargo.toml Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,11 @@ +[package] +name = "rust_package3" +version = "0.1.0" +edition = "2018" + +[lib] +name = "rust_lib3" +crate-type=["staticlib", "cdylib"] + +[[bin]] +name = "rust_bin3" diff -r 2b4f361e3891 -r 629d5123a979 tools/corrosion/test/output directory/output directory/proj3/src/lib.rs --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/corrosion/test/output directory/output directory/proj3/src/lib.rs Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,4 @@ +#[no_mangle] +pub extern "C" fn ret_12() -> u32 { + 12 +} diff -r 2b4f361e3891 -r 629d5123a979 tools/corrosion/test/override_crate_type/CMakeLists.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/corrosion/test/override_crate_type/CMakeLists.txt Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,9 @@ +corrosion_tests_add_test(override_crate_type "cpp-exe;cpp-exe-shared") + +set_tests_properties("override_crate_type_run_cpp-exe" PROPERTIES PASS_REGULAR_EXPRESSION + "^Hello, Cpp! I'm Rust!\r?\n$" + ) + +set_tests_properties("override_crate_type_run_cpp-exe-shared" PROPERTIES PASS_REGULAR_EXPRESSION + "^Hello, Cpp! I'm Rust!\r?\n$" + ) diff -r 2b4f361e3891 -r 629d5123a979 tools/corrosion/test/override_crate_type/override_crate_type/CMakeLists.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/corrosion/test/override_crate_type/override_crate_type/CMakeLists.txt Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,12 @@ +cmake_minimum_required(VERSION 3.15) +project(test_project VERSION 0.1.0) +include(../../test_header.cmake) + +corrosion_import_crate(MANIFEST_PATH rust/Cargo.toml OVERRIDE_CRATE_TYPE my_rust_lib=staticlib,cdylib) + +add_executable(cpp-exe main.cpp) +target_link_libraries(cpp-exe PUBLIC my_rust_lib) + +add_executable(cpp-exe-shared main.cpp) +target_link_libraries(cpp-exe-shared + PUBLIC my_rust_lib-shared) diff -r 2b4f361e3891 -r 629d5123a979 tools/corrosion/test/override_crate_type/override_crate_type/main.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/corrosion/test/override_crate_type/override_crate_type/main.cpp Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,9 @@ +extern "C" void rust_function(char const *name); + +int main(int argc, char **argv) { + if (argc < 2) { + rust_function("Cpp"); + } else { + rust_function(argv[1]); + } +} diff -r 2b4f361e3891 -r 629d5123a979 tools/corrosion/test/override_crate_type/override_crate_type/rust/Cargo.toml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/corrosion/test/override_crate_type/override_crate_type/rust/Cargo.toml Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,10 @@ +[package] +name = "rust-lib" +version = "0.1.0" +authors = ["Andrew Gaspar "] +license = "MIT" +edition = "2018" + + +[lib] +name = "my_rust_lib" diff -r 2b4f361e3891 -r 629d5123a979 tools/corrosion/test/override_crate_type/override_crate_type/rust/build.rs --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/corrosion/test/override_crate_type/override_crate_type/rust/build.rs Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,4 @@ +// Build-scripts also need to be linked, so just add a dummy buildscript ensuring this works. +fn main() { + println!("Build-script is running.") +} diff -r 2b4f361e3891 -r 629d5123a979 tools/corrosion/test/override_crate_type/override_crate_type/rust/src/lib.rs --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/corrosion/test/override_crate_type/override_crate_type/rust/src/lib.rs Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,7 @@ +use std::os::raw::c_char; + +#[no_mangle] +pub extern "C" fn rust_function(name: *const c_char) { + let name = unsafe { std::ffi::CStr::from_ptr(name).to_str().unwrap() }; + println!("Hello, {}! I'm Rust!", name); +} diff -r 2b4f361e3891 -r 629d5123a979 tools/corrosion/test/parse_target_triple/CMakeLists.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/corrosion/test/parse_target_triple/CMakeLists.txt Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,10 @@ +corrosion_tests_add_test(parse_target_triple "") +corrosion_tests_add_test(parse_target_triple_should_fail "") + +set_tests_properties("parse_target_triple_build" PROPERTIES FAIL_REGULAR_EXPRESSION + "CMake Warning" + ) + +set_tests_properties("parse_target_triple_should_fail_build" PROPERTIES PASS_REGULAR_EXPRESSION + "CMake Warning" + ) \ No newline at end of file diff -r 2b4f361e3891 -r 629d5123a979 tools/corrosion/test/parse_target_triple/parse_target_triple/CMakeLists.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/corrosion/test/parse_target_triple/parse_target_triple/CMakeLists.txt Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,106 @@ +# This test is supposed to ensure that the regex in _corrosion_parse_platform works as expected. +cmake_minimum_required(VERSION 3.15) +project(test_project VERSION 0.1.0) +include(../../test_header.cmake) + +# Todo: Test if the output matches expectations. +_corrosion_parse_target_triple("../../blah/x86_64-unknown-custom-gnu.json" arch vendor os env) +_corrosion_parse_target_triple("x86_64-unknown-custom-gnu.json" arch vendor os env) +_corrosion_parse_target_triple("/path/to/x86_64-unknown-custom-musl.json" arch vendor os env) +_corrosion_parse_target_triple("../../blah/x86_64-custom_os.json" arch vendor os env) + +# List of builtin targets aquired via `rustup target list` with rust 1.64 on Linux. +set(rustup_shipped_targets + "aarch64-apple-darwin" + "aarch64-apple-ios" + "aarch64-apple-ios-sim" + "aarch64-fuchsia" + "aarch64-linux-android" + "aarch64-pc-windows-msvc" + "aarch64-unknown-linux-gnu" + "aarch64-unknown-linux-musl" + "aarch64-unknown-none" + "aarch64-unknown-none-softfloat" + "arm-linux-androideabi" + "arm-unknown-linux-gnueabi" + "arm-unknown-linux-gnueabihf" + "arm-unknown-linux-musleabi" + "arm-unknown-linux-musleabihf" + "armebv7r-none-eabi" + "armebv7r-none-eabihf" + "armv5te-unknown-linux-gnueabi" + "armv5te-unknown-linux-musleabi" + "armv7-linux-androideabi" + "armv7-unknown-linux-gnueabi" + "armv7-unknown-linux-gnueabihf" + "armv7-unknown-linux-musleabi" + "armv7-unknown-linux-musleabihf" + "armv7a-none-eabi" + "armv7r-none-eabi" + "armv7r-none-eabihf" + "asmjs-unknown-emscripten" + "i586-pc-windows-msvc" + "i586-unknown-linux-gnu" + "i586-unknown-linux-musl" + "i686-linux-android" + "i686-pc-windows-gnu" + "i686-pc-windows-msvc" + "i686-unknown-freebsd" + "i686-unknown-linux-gnu" + "i686-unknown-linux-musl" + "mips-unknown-linux-gnu" + "mips-unknown-linux-musl" + "mips64-unknown-linux-gnuabi64" + "mips64-unknown-linux-muslabi64" + "mips64el-unknown-linux-gnuabi64" + "mips64el-unknown-linux-muslabi64" + "mipsel-unknown-linux-gnu" + "mipsel-unknown-linux-musl" + "nvptx64-nvidia-cuda" + "powerpc-unknown-linux-gnu" + "powerpc64-unknown-linux-gnu" + "powerpc64le-unknown-linux-gnu" + "riscv32i-unknown-none-elf" + "riscv32imac-unknown-none-elf" + "riscv32imc-unknown-none-elf" + "riscv64gc-unknown-linux-gnu" + "riscv64gc-unknown-none-elf" + "riscv64imac-unknown-none-elf" + "s390x-unknown-linux-gnu" + "sparc64-unknown-linux-gnu" + "sparcv9-sun-solaris" + "thumbv6m-none-eabi" + "thumbv7em-none-eabi" + "thumbv7em-none-eabihf" + "thumbv7m-none-eabi" + "thumbv7neon-linux-androideabi" + "thumbv7neon-unknown-linux-gnueabihf" + "thumbv8m.base-none-eabi" + "thumbv8m.main-none-eabi" + "thumbv8m.main-none-eabihf" + "wasm32-unknown-emscripten" + "wasm32-unknown-unknown" + "wasm32-wasi" + "x86_64-apple-darwin" + "x86_64-apple-ios" + "x86_64-fortanix-unknown-sgx" + "x86_64-fuchsia" + "x86_64-linux-android" + "x86_64-pc-solaris" + "x86_64-pc-windows-gnu" + "x86_64-pc-windows-msvc" + "x86_64-sun-solaris" + "x86_64-unknown-freebsd" + "x86_64-unknown-illumos" + "x86_64-unknown-linux-gnu" + "x86_64-unknown-linux-gnux32" + "x86_64-unknown-linux-musl" + "x86_64-unknown-netbsd" + "x86_64-unknown-none" + "x86_64-unknown-redox" +) +set(other_targets riscv32imc-esp-espidf xtensa-esp32s3-none-elf) + +foreach(target ${rustup_shipped_targets} ${other_targets}) + _corrosion_parse_target_triple("${target}" arch vendor os env) +endforeach() diff -r 2b4f361e3891 -r 629d5123a979 tools/corrosion/test/parse_target_triple/parse_target_triple_should_fail/CMakeLists.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/corrosion/test/parse_target_triple/parse_target_triple_should_fail/CMakeLists.txt Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,6 @@ +# This test is supposed to ensure that the regex in _corrosion_parse_platform works as expected. +cmake_minimum_required(VERSION 3.15) +project(test_project VERSION 0.1.0) +include(../../test_header.cmake) + +_corrosion_parse_target_triple("x86_64-unknown-linux-gnu-toomuch" arch vendor os env) diff -r 2b4f361e3891 -r 629d5123a979 tools/corrosion/test/rust2cpp/CMakeLists.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/corrosion/test/rust2cpp/CMakeLists.txt Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,9 @@ +corrosion_tests_add_test(rust2cpp "cpp-exe;cpp-exe-shared") + +set_tests_properties("rust2cpp_run_cpp-exe" PROPERTIES PASS_REGULAR_EXPRESSION + "^Hello, Cpp! I'm Rust!\r?\n$" + ) + +set_tests_properties("rust2cpp_run_cpp-exe-shared" PROPERTIES PASS_REGULAR_EXPRESSION + "^Hello, Cpp! I'm Rust!\r?\n$" + ) diff -r 2b4f361e3891 -r 629d5123a979 tools/corrosion/test/rust2cpp/rust2cpp/CMakeLists.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/corrosion/test/rust2cpp/rust2cpp/CMakeLists.txt Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,12 @@ +cmake_minimum_required(VERSION 3.15) +project(test_project VERSION 0.1.0) +include(../../test_header.cmake) + +corrosion_import_crate(MANIFEST_PATH rust/Cargo.toml) + +add_executable(cpp-exe main.cpp) +target_link_libraries(cpp-exe PUBLIC rust_lib) + +add_executable(cpp-exe-shared main.cpp) +target_link_libraries(cpp-exe-shared + PUBLIC rust_lib-shared) diff -r 2b4f361e3891 -r 629d5123a979 tools/corrosion/test/rust2cpp/rust2cpp/main.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/corrosion/test/rust2cpp/rust2cpp/main.cpp Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,9 @@ +extern "C" void rust_function(char const *name); + +int main(int argc, char **argv) { + if (argc < 2) { + rust_function("Cpp"); + } else { + rust_function(argv[1]); + } +} diff -r 2b4f361e3891 -r 629d5123a979 tools/corrosion/test/rust2cpp/rust2cpp/rust/Cargo.toml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/corrosion/test/rust2cpp/rust2cpp/rust/Cargo.toml Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,11 @@ +[package] +name = "rust-lib" +version = "0.1.0" +authors = ["Andrew Gaspar "] +license = "MIT" +edition = "2018" + +[dependencies] + +[lib] +crate-type=["staticlib", "cdylib"] diff -r 2b4f361e3891 -r 629d5123a979 tools/corrosion/test/rust2cpp/rust2cpp/rust/build.rs --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/corrosion/test/rust2cpp/rust2cpp/rust/build.rs Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,4 @@ +// Build-scripts also need to be linked, so just add a dummy buildscript ensuring this works. +fn main() { + println!("Build-script is running.") +} diff -r 2b4f361e3891 -r 629d5123a979 tools/corrosion/test/rust2cpp/rust2cpp/rust/src/lib.rs --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/corrosion/test/rust2cpp/rust2cpp/rust/src/lib.rs Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,7 @@ +use std::os::raw::c_char; + +#[no_mangle] +pub extern "C" fn rust_function(name: *const c_char) { + let name = unsafe { std::ffi::CStr::from_ptr(name).to_str().unwrap() }; + println!("Hello, {}! I'm Rust!", name); +} diff -r 2b4f361e3891 -r 629d5123a979 tools/corrosion/test/rustflags/CMakeLists.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/corrosion/test/rustflags/CMakeLists.txt Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,7 @@ +corrosion_tests_add_test(rustflags "rustflags-cpp-exe") + +set_tests_properties("rustflags_run_rustflags-cpp-exe" PROPERTIES PASS_REGULAR_EXPRESSION + "Hello, Cpp! I'm Rust!\r?\nHello, Cpp again! I'm Rust in (Debug|Release) mode again!\r?\nHello, Cpp again! I'm Rust again, third time the charm!\r?\n$" + ) + +corrosion_tests_add_test(cargo_config_rustflags "cargo_config_rustflags") diff -r 2b4f361e3891 -r 629d5123a979 tools/corrosion/test/rustflags/cargo_config_rustflags/.cargo/config.toml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/corrosion/test/rustflags/cargo_config_rustflags/.cargo/config.toml Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,2 @@ +[build] +rustflags = ["--cfg=some_cargo_config_rustflag"] diff -r 2b4f361e3891 -r 629d5123a979 tools/corrosion/test/rustflags/cargo_config_rustflags/CMakeLists.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/corrosion/test/rustflags/cargo_config_rustflags/CMakeLists.txt Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,11 @@ +cmake_minimum_required(VERSION 3.15) +project(test_project VERSION 0.1.0) +include(../../test_header.cmake) + +corrosion_import_crate(MANIFEST_PATH Cargo.toml) + +# Do not use `corrosion_add_target_rustflags()` here, since we want to test if the rustflag from `.cargo/config.toml` +# is picked up. + +# Local rustflags should not interfere with `.cargo/config.toml`, so enable one. +corrosion_add_target_local_rustflags(cargo_config_rustflags "--cfg=local_rustflag") diff -r 2b4f361e3891 -r 629d5123a979 tools/corrosion/test/rustflags/cargo_config_rustflags/Cargo.toml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/corrosion/test/rustflags/cargo_config_rustflags/Cargo.toml Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,4 @@ +[package] +name = "cargo_config_rustflags" +version = "0.1.0" +edition = "2018" diff -r 2b4f361e3891 -r 629d5123a979 tools/corrosion/test/rustflags/cargo_config_rustflags/src/main.rs --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/corrosion/test/rustflags/cargo_config_rustflags/src/main.rs Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,16 @@ + +#[cfg(some_cargo_config_rustflag)] +fn print_line() { + println!("Rustflag is enabled"); +} + +// test that local rustflags don't override global rustflags set via `.cargo/config` +#[cfg(local_rustflag)] +fn test_local_rustflag() { + println!("local_rustflag was enabled"); +} + +fn main() { + print_line(); + test_local_rustflag(); +} diff -r 2b4f361e3891 -r 629d5123a979 tools/corrosion/test/rustflags/rustflags/CMakeLists.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/corrosion/test/rustflags/rustflags/CMakeLists.txt Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,20 @@ +cmake_minimum_required(VERSION 3.15) +project(test_project VERSION 0.1.0) +include(../../test_header.cmake) + +corrosion_import_crate(MANIFEST_PATH rust/Cargo.toml) + +add_executable(rustflags-cpp-exe main.cpp) +target_link_libraries(rustflags-cpp-exe PUBLIC rustflag_test_lib) + +# Test --cfg=key="value" rustflag. +corrosion_add_target_rustflags(rustflag_test_lib --cfg=test_rustflag_cfg1="test_rustflag_cfg1_value") + +# Test using a generator expression to produce a rustflag and passing multiple rustflags. +corrosion_add_target_rustflags(rustflag_test_lib + --cfg=test_rustflag_cfg2="$,$>,debug,release>" + "--cfg=test_rustflag_cfg3" +) + +corrosion_add_target_local_rustflags(rustflag_test_lib "--cfg=test_local_rustflag1") +corrosion_add_target_local_rustflags(rustflag_test_lib --cfg=test_local_rustflag2="value") diff -r 2b4f361e3891 -r 629d5123a979 tools/corrosion/test/rustflags/rustflags/main.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/corrosion/test/rustflags/rustflags/main.cpp Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,13 @@ +extern "C" void rust_function(char const *name); +extern "C" void rust_second_function(char const *name); +extern "C" void rust_third_function(char const *name); + +int main(int argc, char **argv) { + if (argc < 2) { + rust_function("Cpp"); + rust_second_function("Cpp again"); + rust_third_function("Cpp again"); + } else { + rust_function(argv[1]); + } +} diff -r 2b4f361e3891 -r 629d5123a979 tools/corrosion/test/rustflags/rustflags/rust/Cargo.toml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/corrosion/test/rustflags/rustflags/rust/Cargo.toml Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,11 @@ +[package] +name = "rustflag-test-lib" +version = "0.1.0" +license = "MIT" +edition = "2018" + +[dependencies] +some_dependency = { path = "some_dependency" } + +[lib] +crate-type=["staticlib"] diff -r 2b4f361e3891 -r 629d5123a979 tools/corrosion/test/rustflags/rustflags/rust/some_dependency/Cargo.toml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/corrosion/test/rustflags/rustflags/rust/some_dependency/Cargo.toml Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,6 @@ +[package] +name = "some_dependency" +version = "0.1.0" +license = "MIT" +edition = "2018" + diff -r 2b4f361e3891 -r 629d5123a979 tools/corrosion/test/rustflags/rustflags/rust/some_dependency/src/lib.rs --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/corrosion/test/rustflags/rustflags/rust/some_dependency/src/lib.rs Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,10 @@ +//! Test that the local rustflags are only passed to the main crate and not to dependencies. +#[cfg(test_local_rustflag1)] +const _: [(); 1] = [(); 2]; + +#[cfg(test_local_rustflag2 = "value")] +const _: [(); 1] = [(); 2]; + +pub fn some_function() -> u32 { + 42 +} diff -r 2b4f361e3891 -r 629d5123a979 tools/corrosion/test/rustflags/rustflags/rust/src/lib.rs --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/corrosion/test/rustflags/rustflags/rust/src/lib.rs Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,40 @@ +#[cfg(test_rustflag_cfg1 = "test_rustflag_cfg1_value")] +use std::os::raw::c_char; + +#[no_mangle] +#[cfg(test_rustflag_cfg1 = "test_rustflag_cfg1_value")] +pub extern "C" fn rust_function(name: *const c_char) { + let name = unsafe { std::ffi::CStr::from_ptr(name).to_str().unwrap() }; + println!("Hello, {}! I'm Rust!", name); +} + +#[no_mangle] +#[cfg(all(debug_assertions, test_rustflag_cfg2 = "debug"))] +pub extern "C" fn rust_second_function(name: *const c_char) { + let name = unsafe { std::ffi::CStr::from_ptr(name).to_str().unwrap() }; + println!("Hello, {}! I'm Rust in Debug mode again!", name); +} + +#[no_mangle] +#[cfg(all(not(debug_assertions), test_rustflag_cfg2 = "release"))] +pub extern "C" fn rust_second_function(name: *const c_char) { + let name = unsafe { std::ffi::CStr::from_ptr(name).to_str().unwrap() }; + println!("Hello, {}! I'm Rust in Release mode again!", name); +} + +#[no_mangle] +#[cfg(test_rustflag_cfg3)] +pub extern "C" fn rust_third_function(name: *const c_char) { + let name = unsafe { std::ffi::CStr::from_ptr(name).to_str().unwrap() }; + println!("Hello, {}! I'm Rust again, third time the charm!", name); + assert_eq!(some_dependency::some_function(), 42); +} + +#[cfg(not(test_rustflag_cfg3))] +const _: [(); 1] = [(); 2]; + +#[cfg(not(test_local_rustflag1))] +const _: [(); 1] = [(); 2]; + +#[cfg(not(test_local_rustflag2 = "value"))] +const _: [(); 1] = [(); 2]; diff -r 2b4f361e3891 -r 629d5123a979 tools/corrosion/test/workspace/CMakeLists.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/corrosion/test/workspace/CMakeLists.txt Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,6 @@ +corrosion_tests_add_test(workspace "my_program") + +set_tests_properties("workspace_run_my_program" PROPERTIES PASS_REGULAR_EXPRESSION + "^Ok\r?\n$" + ) + diff -r 2b4f361e3891 -r 629d5123a979 tools/corrosion/test/workspace/workspace/CMakeLists.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/corrosion/test/workspace/workspace/CMakeLists.txt Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,32 @@ +cmake_minimum_required(VERSION 3.15) +project(test_project VERSION 0.1.0) +include(../../test_header.cmake) + +corrosion_import_crate( + MANIFEST_PATH ${CMAKE_CURRENT_SOURCE_DIR}/Cargo.toml + CRATES member1 member2 + IMPORTED_CRATES imported_crate_list +) + +#NOTE: member3 also contains a binary called my_program, but that shouldn't be a problem since it is not imported +add_executable(my_program main.cpp) +target_link_libraries(my_program PUBLIC member1 member2) + +# Test that the list of imported crates matches our expectations. +if(NOT DEFINED imported_crate_list) + message(FATAL_ERROR "Corrosion failed to set the variable passed via IMPORTED_CRATES.") +endif() +set(expected_crates member1 member2) +foreach(crate ${expected_crates}) + if(NOT "${crate}" IN_LIST imported_crate_list) + message(FATAL_ERROR "Expected ${crate} to be imported, but it wasn't. Imported crate list:\n" + "${imported_crate_list}" + ) + endif() +endforeach() +set(additional_crates ${imported_crate_list}) +list(REMOVE_ITEM additional_crates ${expected_crates}) +if(additional_crates) + message(FATAL_ERROR "Corrosion unexpectedly imported the following crates: ${additional_crates}") +endif() + diff -r 2b4f361e3891 -r 629d5123a979 tools/corrosion/test/workspace/workspace/Cargo.toml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/corrosion/test/workspace/workspace/Cargo.toml Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,5 @@ +[workspace] +members=["member1", "member2", "member3"] + +[workspace.package] +version = "0.1.0" diff -r 2b4f361e3891 -r 629d5123a979 tools/corrosion/test/workspace/workspace/main.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/corrosion/test/workspace/workspace/main.cpp Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,4 @@ +#include +int main() { + std::cout << "Ok"; +} diff -r 2b4f361e3891 -r 629d5123a979 tools/corrosion/test/workspace/workspace/member1/Cargo.toml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/corrosion/test/workspace/workspace/member1/Cargo.toml Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,8 @@ +[package] +name = "member1" +version = "0.1.0" +edition = "2018" +description = "descr;\"hello\\" + +[lib] +crate-type = [ "lib", "cdylib" ] diff -r 2b4f361e3891 -r 629d5123a979 tools/corrosion/test/workspace/workspace/member1/src/lib.rs --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/corrosion/test/workspace/workspace/member1/src/lib.rs Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,7 @@ +#[cfg(test)] +mod tests { + #[test] + fn it_works() { + assert_eq!(2 + 2, 4); + } +} diff -r 2b4f361e3891 -r 629d5123a979 tools/corrosion/test/workspace/workspace/member2/Cargo.toml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/corrosion/test/workspace/workspace/member2/Cargo.toml Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,8 @@ +[package] +name = "member2" +version = "0.1.0" +authors = ["Olivier Goffart "] +edition = "2018" + +[lib] +crate-type = ["staticlib"] diff -r 2b4f361e3891 -r 629d5123a979 tools/corrosion/test/workspace/workspace/member2/src/lib.rs --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/corrosion/test/workspace/workspace/member2/src/lib.rs Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,7 @@ +#[cfg(test)] +mod tests { + #[test] + fn it_works() { + assert_eq!(2 + 2, 4); + } +} diff -r 2b4f361e3891 -r 629d5123a979 tools/corrosion/test/workspace/workspace/member3/Cargo.toml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/corrosion/test/workspace/workspace/member3/Cargo.toml Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,14 @@ +[package] +name = "member3" +version = "0.1.0" +authors = ["Olivier Goffart "] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[[bin]] +name = "my_program" +path = "src/main.rs" + +[dependencies] +member1 = { path = "../member1" } diff -r 2b4f361e3891 -r 629d5123a979 tools/corrosion/test/workspace/workspace/member3/src/main.rs --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/corrosion/test/workspace/workspace/member3/src/main.rs Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,3 @@ +fn main() { + println!("Hello, world!"); +} diff -r 2b4f361e3891 -r 629d5123a979 tools/map_templates_tool/CMakeLists.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/map_templates_tool/CMakeLists.txt Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,33 @@ +cmake_minimum_required(VERSION 3.16) + +project(map_templates_tool VERSION 1.0 LANGUAGES CXX) + +set(CMAKE_AUTOMOC ON) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +find_package(Qt6 6.2 COMPONENTS Quick REQUIRED) + +qt_add_executable(appmap_templates_tool + main.cpp +) + +qt_add_qml_module(appmap_templates_tool + URI map_templates_tool + VERSION 1.0 + QML_FILES main.qml +) + +set_target_properties(appmap_templates_tool PROPERTIES + MACOSX_BUNDLE_GUI_IDENTIFIER my.example.com + MACOSX_BUNDLE_BUNDLE_VERSION ${PROJECT_VERSION} + MACOSX_BUNDLE_SHORT_VERSION_STRING ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR} + MACOSX_BUNDLE TRUE + WIN32_EXECUTABLE TRUE +) + +target_link_libraries(appmap_templates_tool + PRIVATE Qt6::Quick) + +install(TARGETS appmap_templates_tool + BUNDLE DESTINATION . + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}) diff -r 2b4f361e3891 -r 629d5123a979 tools/map_templates_tool/main.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/map_templates_tool/main.cpp Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,19 @@ +#include +#include + + +int main(int argc, char *argv[]) +{ + QGuiApplication app(argc, argv); + + QQmlApplicationEngine engine; + const QUrl url(u"qrc:/map_templates_tool/main.qml"_qs); + QObject::connect(&engine, &QQmlApplicationEngine::objectCreated, + &app, [url](QObject *obj, const QUrl &objUrl) { + if (!obj && url == objUrl) + QCoreApplication::exit(-1); + }, Qt::QueuedConnection); + engine.load(url); + + return app.exec(); +} diff -r 2b4f361e3891 -r 629d5123a979 tools/map_templates_tool/main.qml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/map_templates_tool/main.qml Tue Dec 31 15:19:43 2024 +0100 @@ -0,0 +1,258 @@ +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts +import QtQuick.Shapes + +Window { + id: control + + width: 1024 + height: 768 + visible: true + title: qsTr("Map Templates") + + property bool hasError: false + + Page { + id: page + anchors.fill: parent + + Rectangle { + id: mapContainer + + property int spaceForCode: Math.max(250, parent.height * 0.6) + property int mapWidth: 2048 + property int mapHeight: 1024 + property real aspectRatio: mapWidth / mapHeight + property bool fitWidth: aspectRatio > (parent.width / (parent.height - spaceForCode)) + + implicitWidth: fitWidth ? parent.width : (parent.height - spaceForCode) * aspectRatio + implicitHeight: fitWidth ? parent.width / aspectRatio : (parent.height - spaceForCode) + + x: (parent.width - width) / 2 + + border.width: 2 + border.color: hasError ? "red" : "black" + } + + Shape { + id: shape + + anchors.fill: mapContainer + } + + MouseArea { + id: mouseArea + anchors.fill: mapContainer + hoverEnabled: true + } + + Label { + text: mouseArea.containsMouse ? "(%1, %2)".arg(Math.floor(mouseArea.mouseX / mouseArea.width * mapContainer.mapWidth)).arg(Math.floor(mouseArea.mouseY / mouseArea.height * mapContainer.mapHeight)) : "" + } + + Rectangle { + anchors.fill: codeInput + color: "gray" + } + + TextEdit { + id: codeInput + + anchors.bottom: parent.bottom + anchors.left: parent.left + anchors.right: parent.right + height: parent.height - mapContainer.height + + text: " - + width: 3072 + height: 1424 + can_flip: false + can_invert: false + can_mirror: true + is_negative: false + put_girders: true + max_hedgehogs: 18 + outline_points: + - + - {x: 748, y: 1424, w: 1, h: 1} + - {x: 636, y: 1252, w: 208, h: 72} + - {x: 898, y: 1110, w: 308, h: 60} + - {x: 1128, y: 1252, w: 434, h: 40} + - {x: 1574, y: 1112, w: 332, h: 40} + - {x: 1802, y: 1238, w: 226, h: 36} + - {x: 1930, y: 1424, w: 1, h: 1} + - + - {x: 2060, y: 898, w: 111, h: 111} + - {x: 1670, y: 876, w: 34, h: 102} + - {x: 1082, y: 814, w: 284, h: 132} + - {x: 630, y: 728, w: 126, h: 168} + - {x: 810, y: 574, w: 114, h: 100} + - {x: 1190, y: 572, w: 352, h: 120} + - {x: 1674, y: 528, w: 60, h: 240} + - {x: 1834, y: 622, w: 254, h: 116} + fill_points: + - {x: 1423, y: 0} +" + + onTextChanged: { + const template = parseInput() + + if (template) { + mapContainer.mapWidth = Number(template.width) + mapContainer.mapHeight = Number(template.height) + + shape.data = renderTemplate(template) + } + } + } + } + + function parseInput() { + let code = codeInput.text.split('\n') + + if(code[0] !== " -") { + hasError = true + return + } + + code = code.slice(1) + code.push("") + + let parsed = ({}) + let polygonAccumulator = [] + let pointAccumulator = [] + let key = "" + code.forEach(line => { + let newKey + if (line === " outline_points:") { + newKey = "outline_points" + } + + if (line === " walls:") { + newKey = "walls" + } + + if (line === " fill_points:") { + newKey = "fill_points" + } + + if (line === "") { + newKey = "_" + } + + if (key === "fill_points" && line.startsWith(" - {")) { + // ignore + return + } + + if (newKey) { + if (key.length > 0) { + polygonAccumulator.push(pointAccumulator) + parsed[key] = polygonAccumulator + } + + key = newKey + polygonAccumulator = [] + pointAccumulator = [] + + return + } + + if (line === " -") { + if (pointAccumulator.length > 0) { + polygonAccumulator.push(pointAccumulator) + pointAccumulator = [] + } + + return + } + + const matchValue = line.match(/^\s{4}(\w+):\s(.+)$/); + + if (matchValue) { + parsed[matchValue[1]] = matchValue[2] + return + } + + const matchPoint = line.match(/^\s{8}-\s\{([^}]+)\}$/); + + if (matchPoint) { + const point = matchPoint[1].split(", ").reduce((obj, pair) => { + const [key, value] = pair.split(": "); + obj[key] = isNaN(value) ? value : parseInt(value); + return obj; + }, {}) + pointAccumulator.push(point) + return + } + + console.log("Unrecognized: " + JSON.stringify(line)) + hasError = true + throw "" + }) + + hasError = false + + return parsed + } + + Component { + id: shapePathComponent + + ShapePath { + fillColor: "transparent" + scale: Qt.size(mapContainer.width / mapContainer.mapWidth, mapContainer.height / mapContainer.mapHeight) + strokeWidth: 3 + } + } + + Component { + id: pathLineComponent + PathLine { + } + } + + function polygons2shapes(polygons, lineColor, rectColor) { + if (!Array.isArray(polygons)) { + return [] + } + + let rectangles = [] + + polygons.forEach(polygon => polygon.forEach(r => { + let shapePath = shapePathComponent.createObject(shape) + shapePath.strokeWidth = 1 + shapePath.strokeColor = rectColor + + shapePath.startX = r.x + shapePath.startY = r.y + shapePath.pathElements = [ + pathLineComponent.createObject(shapePath, {x: r.x, y: r.y + r.h}), + pathLineComponent.createObject(shapePath, {x: r.x + r.w, y: r.y + r.h}), + pathLineComponent.createObject(shapePath, {x: r.x + r.w, y: r.y}), + pathLineComponent.createObject(shapePath, {x: r.x, y: r.y}) + ] + + rectangles.push(shapePath) + })) + let polygonShapes = polygons.map(polygon => { + let points = polygon.map(r => ({x: r.x + r.w / 2, y: r.y + r.h / 2})) + let shapePath = shapePathComponent.createObject(shape) + let start = points[points.length - 1] + + shapePath.strokeColor = lineColor + shapePath.startX = start.x + shapePath.startY = start.y + shapePath.pathElements = points.map(p => pathLineComponent.createObject(shapePath, p)) + + return shapePath + }) + + return rectangles.concat(polygonShapes) + } + + function renderTemplate(template) { + return polygons2shapes(template.outline_points, "red", "black").concat(polygons2shapes(template.walls, "gray", "gray")) + } +} diff -r 2b4f361e3891 -r 629d5123a979 tools/pas2c/PascalParser.hs --- a/tools/pas2c/PascalParser.hs Sat Sep 28 22:27:13 2024 +0200 +++ b/tools/pas2c/PascalParser.hs Tue Dec 31 15:19:43 2024 +0100 @@ -346,6 +346,7 @@ , try $ string "overload;" , try $ string "export;" , try $ string "varargs;" + , try $ string "external;" , try (string' "external") >> comments >> iD >> comments >> optional (string' "name" >> comments >> stringLiteral pas) >> string' ";" >> return "external;" ]