Xcode 9.1 upgrade to Swift 4 breaks #ifdef - swift

I've got a project written in Swift 3 that has a number of #if ... #elses scattered throughout; they just check for a certain variable (defined with the -D compiler flag) which is set by my xcode project to know if the project is being built in xcode or with the package manager and does some imports accordingly. For example,
#if XCODE_BUILD
// do some imports that work when built with xcode
#else
// do some imports that won't work when built with xcode
#endif
The code builds just fine via either method.
But, when I select the option to upgrade to Swift 4 (either of the options offered -- "Minimize inference" or "Match Swift 3 behavior"), the code fails to compile so the migration fails. It appears that the #ifs are not being respected or the XCODE_BUILD variable isn't being defined, since the failures occur in the imports that shouldn't happen when being built from Xcode.
Does Swift 4 do something different with #ifs? Does Xcode somehow not define the compiler flags while doing the migration?

You can use #if, #else and #endif, resulting in:
#if XCODE_BUILD
// do some imports that work when built with xcode
#else
// do some imports that won't work when built with xcode
#endif
Apple docs here.
Another answer with some additional details can be found here: https://stackoverflow.com/a/24152730/118091

Previously, I was using the 'Other Swift Flags' build setting in Xcode to pass '-DXCODE_BUILD'. Apparently that setting doesn't work for Swift 4. The new setting that does work is 'Active Compilation Conditions' (it should be set to include XCODE_BUILD, no need for the -D flag).

Related

Swift macros not detected

After reading this stackoverflow post, I tried to introduce macros in my project.
I have the following code in a sample macOS CommandLine tool.
#if ELDIABLO
NSLog ("ELDIABLO macro detected!")
#else
NSLog ("ELDIABLO macro not detected!")
#endif
The ELDIABLO macro is declared in Target->BuildSettings->SwiftCompiler/OtherSwiftFlags (prefixed with -D).
This works.
SwiftMacros[73110:12048088] Detected ELDIABLO macro!!
Now, when I transferred the same concept to my original project it doesn't work. I always get
ELDIABLO macro not detected!
According to another stackoverflow post, the macros should be defined in Target->BuildSettings->SwiftCompiler/ActiveCompilationConditions (without -D prefix).
I tried that too, but didn't work.
What's wrong here? What am I missing?
I'm using Xcode 13.4.
My project structure: One target (the app) dependent on many other targets (static libs). All macro settings are applied to the app target (not to the static libs).
I was setting the macro in the app target, but the code which uses macro is one of the other targets i.e static libs, though the static libs targets are added as a dependency for the app target.
After adding the macro to the static lib target, it works.
That means the macro didn't find your "Condition setting", ex: ELDIABLO
So now you need to open your Project/[Your in-use target]/Build Settings.
Then, search the setting with the keyword: "active compilation". And, put your CONDITION_CONFIG here. One line for one CONDITION_CONFIG.
Check image below:

Disable "show live issues" in conditionally compiled code (Swift + Xcode 11)?

I'm guessing this is a bug of sorts (and if so I'll file a report)...
If you have "show live issues" enabled in Xcode's Preferences, issues can be flagged even in code that won't be compiled because of a conditional compilation block.
Here's a trivial example. When using Xcode 11.5 or earlier the "!!!" causes a syntax error to be flagged even though that code is never compiled:
#if swift(>=5.3)
print("Swift >= 5.3")
!!! // <-- causes "show live issues" to complain
#else
print("Swift < 5.3")
#endif
Is there any workaround for this that people are aware of?
A "real" example might use syntax that is only available with Swift 5.3+ within the first section and < 5.3 syntax in the second section.
Note that the code will actually compile and run but "show live issues" is getting tripped up by the presence of "invalid" code and ideally it wouldn't check code that won't be compiled.
Note that Objective-C doesn't have this issue, the following doesn't result in any "show live issues" warning if XCODE12 isn't defined:
#if defined(XCODE12)
NSLog(#"XCODE12");
!!!
#else
NSLog(#"XCODE11");
#endif

stm32f469 FreeRTOS+STemWin with CubeMX and keil: Bad operand types (UnDefOT, Constant) for operator (*

I'm making project generated with STM32CubeMX for stm32f469i-disco.
I have based setup on "FreeRTOSconfig.h" from "Demonstration" project in STM32CubeFWF4V1.16.0 repo. Fresh project from CubeMX compiles without problems, but after adding STemWin lib "STemWin532_CM4_OS_Keil_ot.lib" I get the error
"..........\Middlewares\ThirdParty\FreeRTOS\Source\portable\RVDS\ARMCM4F\port.c(507):
error: A1586E: Bad operand types (UnDefOT, Constant) for operator ("
Interesting fact is that "Demonstration" compiles without this error.
How do I get the project configured:
In CubeMX:
Add periphs needed: DMA2D, DSIHost, FMC etc.
Add BSP drivers for touchscreen etc.
Now, it copiles without problems.
Clone "Clock Configuration" based on "Demonstations"
In "Configuration" tab I clone all configuration based on code from "Demonstrations"
Then in Keil:
5. I update Include Path in target options.
Add all STemWin files and when i try to compile:
"...Bad operand types..."
When I disable freertos in CubeMX and add non-OS STemWin lib it compiles without problems.
When I try to compile non_OS STemWin lib with FreeRTOS enabled, it fails with the same message.
What have I tried to do?
Update port.c. Nothing changed.
Am I missing something while creating project?
After spending 2 days to find out what might be the reason for this error, I just found it and my project now compiles with FreeRTOS enabled and all the other sources used initially. Well, it's apparently a recursive include for stm32f4xx_hal.h file. I have added some modules from the demo package and those have some dependencies. Because I wanted to strip out some functions from some modules, I have manually added the includes/resources for the functions needed, which has contributed to that error, as I did not add any guards to my includes.
The offending line was in port.c file, and the error was because of a wrong constant value passed to the assembly line 483: mov r0, #configMAX_SYSCALL_INTERRUPT_PRIORITY
Replacing configMAX_SYSCALL_INTERRUPT_PRIORITY with any numeric value has helped in compiling the code, but I thought I should not leave it like that and it would be much better if I found out the reason for the problem.
More could be found here:
https://community.st.com/thread/44751-portc483-error-a1586e-bad-operand-types-undefot-constant-for-operator
I got a same problem while building a Nucleo-F401RE board firmware with CubeMX 4.25.
This problem started just after setting LL driver use (Low-Level driver) instead of HAL in a CubeMX setting dialog.
I think this problem is from complicit in C definition of header file.
Including LL driver header may change __NVIC_PRIO_BITS definition in somewhere.
#ifdef __NVIC_PRIO_BITS
#define configPRIO_BITS __NVIC_PRIO_BITS
#else
#define configPRIO_BITS 4
#endif
#define configMAX_SYSCALL_INTERRUPT_PRIORITY (configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY << (8 - configPRIO_BITS))
Changing __NVIC_PRIO_BITS to 4 in a generated source code will solve the problem.
/* mov r0, #configMAX_SYSCALL_INTERRUPT_PRIORITY */
mov r0, #(configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY << (8 - 4))

identically-named classes in app and lib cause issues *after* converting from Makefile to cmake

I'm trying to convert a program and its plugin from custom Makefiles to CMake, with minimal changes to the code.
Both the plugin and the app share some code; #ifdef ... #else ... #endif blocks are used where there are differences, and I'm certain the code is compiled with the correct defines. The shared code includes a class called ToolImage. When the code is compiled for the app, the ToolImage constructor uses a different resource path than when it is compiled for the plugin.
#ifdef THE_APP
ToolImage::ToolImage(const wxString& name, bool full_path_given):wxImage(full_path_given?name:
(wxGetApp().GetResFolder() + _T("/bitmaps/") + name + _T(".png")), wxBITMAP_TYPE_PNG)
#else
ToolImage::ToolImage(const wxString& name, bool full_path_given):wxImage(full_path_given?name:
(theApp.GetResFolder() + _T("/bitmaps/") + name + _T(".png")), wxBITMAP_TYPE_PNG)
#endif
{
...
}
When the program and its plugin have been compiled with the custom Makefiles, everything works as expected. When both have been compiled with CMake, using a series of CMakeLists.txt files I created, there is an issue: the plugin isn't able to load the bitmaps for its toolbar.
I tracked the problem to the ToolImage class. The line number given by gdb tells me that the plugin is using the wrong constructor. strace tells me the same thing (the plugin is looking for its bitmaps in the app's resource dir rather than in the plugin's resource dir). To ensure that I didn't have the defines screwed up, I put a #error in ToolImage.cpp, inside the part of the #ifdef that should only be compiled for the app - and the plugin still compiled without error. This tells me that the plugin is compiling with the correct code. Since it is using the wrong path, I think it is using the class and constructor compiled into the program instead of its own.
How do I ensure that the plugin uses its own ToolImage class instead of the one in the app?! I don't own the project and don't want to make massive changes merely to support building with a different build system.
Using the precompiler to create two versions of a class seems like a poor choice to me. If I must make changes to the code, do you have suggestions for a workaround?
For the sake of experiment, I'd add -fvisibility=hidden when building theapp, to all or maybe to some specific sources. This should hide application's ToolImage from the plugin.
It is not a universal method, as in many cases plugins do use different symbols from the main executable.
I fixed this by adding the linker flag -Wl,-Bsymbolic-functions in the CMakeLists.txt:
set_target_properties( heekscnc PROPERTIES LINK_FLAGS -Wl,-Bsymbolic-functions )

Which conditional compile to use to switch between Mac and iPhone specific code?

I am working on a project that includes a Mac application and an iPad application that share code. How can I use conditional compile switches to exclude Mac-specific code from the iPhone project and vice-versa? I've noticed that TARGET_OS_IPHONE and TARGET_OS_MAC are both 1, and so they are both always true. Is there another switch I can use that will only return true when compiling for a specific target?
For the most part, I've gotten the files to cooperate by moving #include <UIKit/UIKit.h> and #include <Cocoa/Cocoa.h> into the precompile headers for the two projects. I'm sharing models and some utility code that fetches data from RSS feeds and Evernote.
In particular, the [NSData dataWithContentsOfURL:options:error:] function takes a different constant for the options parameter iOS 3.2 and earlier and Mac OS 10.5 and earlier than it does for iOS 4 and Mac OS 10.6. The conditional I'm using is:
#if (TARGET_OS_IPHONE && (__IPHONE_OS_VERSION_MAX_ALLOWED > __IPHONE_3_2)) || (TARGET_OS_MAC && (MAC_OS_X_VERSION_MIN_REQUIRED > MAC_OS_X_VERSION_10_5))
This seems to work, but I want to make sure this is bulletproof. My understanding is that if the Mac version is set to 10.6, but the iOS version is set to 3.2, it will still use the new constants even if it's compiling for iOS 3.2, which seems incorrect.
Thanks in advance for any help!
You've made a mistake in your observations. :)
TARGET_OS_MAC will be 1 when building a Mac or iPhone application. You're right, it's quite useless for this sort of thing.
However, TARGET_OS_IPHONE is 0 when building a Mac application. I use TARGET_OS_IPHONE in my headers all the time for this purpose.
Like this:
#if TARGET_OS_IPHONE
// iOS code
#else
// OSX code
#endif
Here's a great chart on this:
http://sealiesoftware.com/blog/archive/2010/8/16/TargetConditionalsh.html
The macros to use are defined in the SDK header file TargetConditionals.h. Taken from the 10.11 SDK:
TARGET_OS_WIN32 - Generated code will run under 32-bit Windows
TARGET_OS_UNIX - Generated code will run under some Unix (not OSX)
TARGET_OS_MAC - Generated code will run under Mac OS X variant
TARGET_OS_IPHONE - Generated code for firmware, devices, or simulator
TARGET_OS_IOS - Generated code will run under iOS
TARGET_OS_TV - Generated code will run under Apple TV OS
TARGET_OS_WATCH - Generated code will run under Apple Watch OS
TARGET_OS_SIMULATOR - Generated code will run under a simulator
TARGET_OS_EMBEDDED - Generated code for firmware
Since everything is a “Mac OS X variant” here, TARGET_OS_MAC is not useful in this case. To compile specifically for macOS, for example:
#if !TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR && !TARGET_OS_EMBEDDED
// macOS-only code
#endif
Update: Newer headers (Xcode 8+?) now have TARGET_OS_OSX defined specifically for macOS. (h/t #OldHorse), so this should work:
#if TARGET_OS_OSX
// macOS-only code
#endif
"The correct thing to do is just use the newer constants, because if you look at the header you will see they are declared equivalent to the old ones in the enum, which means the new constants will work even on the old releases (both constants compile to the same thing, and since enums are compiled into the app they can't change without breaking binary compatibility). The only reason not to do that is if you need to continue building agains the older SDKs (that is a different thing than supporting older releases, which you can do while compiling against the newer SDKs).
If you actually wanted to use different flags based on the OS version (because the new version actually added new functionality, as opposed to just renaming a constant) then there are two sensible things you can do, neither of which your above macro accomplishes:
To always use the old flags unless the min version allowed is greater than version they were introduced in (something like this):
#if (__IPHONE_OS_VERSION_MIN_REQUIRED >= 40000 || __MAC_OS_X_VERSION_MIN_REQUIRED >= 1060)
NSDataReadingOptions options = NSDataReadingMapped;
#else
NSDataReadingOptions options = NSMappedRead;
#end
Conditionally use only the new values in builds that can on only the new versions, and compile in code to determine the flags at runtime for builds that support both versions:
#if (__IPHONE_OS_VERSION_MIN_REQUIRED >= 40000 || __MAC_OS_X_VERSION_MIN_REQUIRED >= 1060)
NSDataReadingOptions options = NSDataReadingMapped;
#else
NSDataReadingOptions options;
if ([[UIDevice currentDevice] systemVersion] compare:#"4.0"] != NSOrderedAscending) {
options = NSDataReadingMapped;
} else {
options = NSMappedRead;
}
#end
Note that if you actually were doing this comparison a lot you would want to stash the result of the [[UIDevice currentDevice] systemVersion] compare:#"4.0"] somewhere. You also generally want to explicitly test for features using things like weak linking instead of doing version compares, but that is not an option for enums.
The set of macros to use includes now TARGET_OS_OSX:
TARGET_OS_WIN32 - Generated code will run under 32-bit Windows
TARGET_OS_UNIX - Generated code will run under some Unix (not OSX)
TARGET_OS_MAC - Generated code will run under Mac OS X variant
TARGET_OS_OSX - Generated code will run under OS X devices
TARGET_OS_IPHONE - Generated code for firmware, devices, or simulator
TARGET_OS_IOS - Generated code will run under iOS
TARGET_OS_TV - Generated code will run under Apple TV OS
TARGET_OS_WATCH - Generated code will run under Apple Watch OS
TARGET_OS_BRIDGE - Generated code will run under Bridge devices
TARGET_OS_SIMULATOR - Generated code will run under a simulator
TARGET_OS_EMBEDDED - Generated code for firmware
Seems to work ok for conditional compilation of macOS code.