I'm working on implementation of a swift package with an adaptive logger that can determine environment for output purposes. The logger supports all platforms (macOS, iOS, tvOS etc.) and can be used from Tests or Swift command line apps as well.
My question: how to determine that the Swift code is being run under Terminal app for instance when run swift test?
I've found that ProcessInfo's environment property has a "_" field with "/usr/bin/swift" or "/Users/.../TestApp" values when you use the Terminal app but I'm not sure that is a correct approach.
var isTerminal : Bool {
return ProcessInfo.processInfo.environment["_"] != nil
}
Are there any other approaches to check this?
Instead of trying to detect whether you're using Terminal.app (which could be error-prone if you're e.g. using a different terminal program or running on Linux), you should instead query the terminal to find out whether it supports specific features. This can be done easily with the ncurses library.
1. Add a CCurses target to your Swift package
We need to create a system library target so that the Swift Package Manager knows how to find and link against the ncurses library Add the target to your Package.swift:
let package = Package(
// ...
targets: [
// ...
.systemLibrary(name: "CCurses"),
],
// ...
)
And create a new directory at Sources/CCurses with the following files:
curses.h
#include <curses.h>
#include <term.h>
module.modulemap
module CCurses [system] {
header "curses.h"
link "curses"
export *
}
2. Check if the terminal supports color output
When you're setting up, query the terminfo database to check for color support:
import CCurses
// ...
var erret: Int32 = 0
if setupterm(nil, 1, &erret) != ERR {
useColor = has_colors()
} else {
useColor = false
}
Based on https://stackoverflow.com/a/7426284.
If you need to check for the existence of another feature, there's almost certainly an ncurses function for it -- just check the man page, search online, or ask here on SO.
The ncurses dylib is available on all platforms, but for some reason the headers only exist on macOS. You shouldn't need it on iOS, watchOS, or tvOS anyway, because those platforms don't have terminals. There are two ways to exclude ncurses from being built on iOS, etc: 1) you can #ifdef out the headers, or 2) with Swift 5.3 you can declare a conditional target dependency, which is slightly simpler and cleaner.
Approach 1: #ifdef the headers
Use the following Sources/CCurses/curses.h file instead:
#ifdef __APPLE__
#include <TargetConditionals.h>
#endif
#if !(defined TARGET_OS_IPHONE || defined TARGET_OS_WATCH || defined TARGET_OS_TV)
#include <curses.h>
#include <term.h>
#endif
Then, whenever you use a function from curses in your Swift code, surround it with a build conditional:
#if os(iOS) || os(watchOS) || os(tvOS)
useColor = false
#else
var erret: Int32 = 0
if setupterm(nil, 1, &erret) != ERR {
useColor = has_colors()
} else {
useColor = false
}
#endif
Approach 2: conditional target dependency (requires Swift 5.3)
No changes are necessary to the CCurses target; you only have to modify your dependency on CCurses in your Package.swift:
.target(
name: "MyLib",
dependencies: [
.target(name: "CCurses", condition: .when(platforms: [.macOS, .linux]))
]),
And use a build condition whenever you import CCurses or use curses functions:
#if canImport(CCurses)
import CCurses
#endif
// ...
#if canImport(CCurses)
var erret: Int32 = 0
if setupterm(nil, 1, &erret) != ERR {
useColor = has_colors()
} else {
useColor = false
}
#else
useColor = false
#endif
Related
I'm trying to use a callback function with the led controller of esp32, however I'm unable to compile the code. I'm not sure if something is missing or the code has errors, as I have a limited understanding of pointers or coding in general.
I'm using the Arduino framework, however when I hover over the ledc_cb_register text, VSCode will popup some more details/definition of this function, so I would expect that it does see the reference to it.
relevant esp32 documentation:
docs.espressif.com
I'm trying to copy the following example, but make it a bit simpler (using only one channel):
github
It seems this example can be compiled on my side too, but this uses espidf framework.
trying the following code (many lines are not shown here for simplicity)
static bool cb_ledc_fade_end_event(const ledc_cb_param_t *param, void *user_arg)
{
portBASE_TYPE taskAwoken = pdFALSE;
if (param->event == LEDC_FADE_END_EVT) {
isFading = false;
}
return (taskAwoken == pdTRUE);
}
[...]
void setup() {
ledc_timer_config_t ledc_timer = {
.speed_mode = LEDC_HIGH_SPEED_MODE, // timer mode
.duty_resolution = LEDC_TIMER_13_BIT, // resolution of PWM duty
.timer_num = LEDC_TIMER_0, // timer index
.freq_hz = LED_frequency, // frequency of PWM signal
.clk_cfg = LEDC_AUTO_CLK, // Auto select the source clock
};
ESP_ERROR_CHECK(ledc_timer_config(&ledc_timer));
ledc_channel_config_t ledc_channel = {
.gpio_num = LED_PIN,
.speed_mode = LEDC_HIGH_SPEED_MODE,
.channel = LEDC_CHANNEL_0,
.timer_sel = LEDC_TIMER_0,
.duty = 4000,
.hpoint = 0,
//.flags.output_invert = 0
};
ESP_ERROR_CHECK(ledc_channel_config(&ledc_channel));
ledc_fade_func_install(0);
ledc_cbs_t callbacks = {
.fade_cb = cb_ledc_fade_end_event
};
ledc_cb_register(LEDC_HIGH_SPEED_MODE, LEDC_CHANNEL_0, &callbacks, 0);
and getting the following error message:
[..]/.platformio/packages/toolchain-xtensa-esp32#8.4.0+2021r2-patch3/bin/../lib/gcc/xtensa-esp32-elf/8.4.0/../../../../xtensa-esp32-elf/bin/ld.exe: .pio\build\esp32dev\src\main.cpp.o:(.literal._Z5setupv+0x78): undefined reference to 'ledc_cb_register(ledc_mode_t, ledc_channel_t, ledc_cbs_t*, void*)'
[..]/.platformio/packages/toolchain-xtensa-esp32#8.4.0+2021r2-patch3/bin/../lib/gcc/xtensa-esp32-elf/8.4.0/../../../../xtensa-esp32-elf/bin/ld.exe: .pio\build\esp32dev\src\main.cpp.o: in function 'setup()':
[..]\PlatformIO\Projects\asdf/src/main.cpp:272: undefined reference to 'ledc_cb_register(ledc_mode_t, ledc_channel_t, ledc_cbs_t*, void*)'
collect2.exe: error: ld returned 1 exit status
*** [.pio\build\esp32dev\firmware.elf] Error 1
According to the docs, it seems to be a feature that was added in v4.4.4
but the latest Arduino core (2.0.6) is build on v4.4.3.
If you are not on the latest Arduino core, try updating that first and see if it works. If not, then you just have to wait until the Arduino core is updated to use ESP IDF v4.4.4.
Of course, you can use ledc_isr_register(...) to register an ISR handler for the interrupt.
Best of luck!
Update:
I realized that the problem (at least on my side when testing it) was that there is an error in the ledc.h file, where they forgot to add ledc_cb_register in an extern "C" block.
I manually patched it by moving the
#ifdef __cplusplus
}
#endif
part, which was located after the ledc_set_fade_step_and_start function, below ledc_cb_register instead.
So, the end of my ledc.h file looks like this now:
...
esp_err_t ledc_set_fade_step_and_start(ledc_mode_t speed_mode, ledc_channel_t channel, uint32_t target_duty, uint32_t scale, uint32_t cycle_num, ledc_fade_mode_t fade_mode);
/**
* #brief LEDC callback registration function
* ...
*/
esp_err_t ledc_cb_register(ledc_mode_t speed_mode, ledc_channel_t channel, ledc_cbs_t *cbs, void *user_arg);
#ifdef __cplusplus
}
#endif
CGo appears to be removing preprocessor definitions. Has anyone else run into this?
In my other C code on the same machine, this works fine. In CGo, the "__AVX2__" definition gets removed.
I've tried this with and without the '-march=native'
How can I get it back ?
The code:
//go:build amd64 && !purego && gc
// +build amd64,!purego,gc
// #cgo CFLAGS: -march=native
package keccakAVX512
/*
// check if the amd64 machine supports AVX512 instructions at build time and call
// an assembly function using AVX512 if so.
#ifdef __AVX2__
void keccakF1600_AVX512(unsigned long* state);
this should fail with a syntax error but doesn't
#endif
// void keccakF1600_AVX512(unsigned long* state) {};
*/
import "C"
func KeccakF1600AMDAVX512(a *[25]uint64) {
C.keccakF1600_AVX512((*C.ulong)(&a[0]))
}
With the 'go tool cgo' command, I see that the default '-m64' is taking precedence, even with the CFLAGS set.
I found that the problem was in the #cgo syntax. It has to be inside the '/**/' comment block instead of the '//' comment block.
package keccakAVX512
/*
#cgo CFLAGS: -march=native
// check if the amd64 machine supports AVX512 instructions at build time and call
// an assembly function using AVX512 if so.
#ifdef __AVX2__
void keccakF1600_AVX512(unsigned long* state);
#else
void keccakF1600_AVX512(unsigned long* state) {};
#endif
*/
import "C"
func KeccakF1600AMDAVX512(a *[25]uint64) {
C.keccakF1600_AVX512((*C.ulong)(&a[0]))
}
I'm trying to move one of my apps over to using Swift. It contains an OpenGL draw loop (that also contains some Cocoa statements - yes, I realise it's probably a horrible mess of a class) so I've copied the original .m & .h files into my new project and added a *-Bridging-Header.h file. I've also added a build phase to link with the OpenGL.framework (although I'm not sure I needed to and it made no difference to the issue).
Originally I borrowed heavily from Apple's example OpneGL project and within one of the files I'm trying to compile there is:
#include "sourceUtil.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#import <OpenGL/OpenGL.h>
demoSource* srcLoadSource(const char* filepathname)
{
demoSource* source = (demoSource*) calloc(sizeof(demoSource), 1);
// Check the file name suffix to determine what type of shader this is
const char* suffixBegin = filepathname + strlen(filepathname) - 4;
if(0 == strncmp(suffixBegin, ".fsh", 4))
{
source->shaderType = GL_FRAGMENT_SHADER;
}
else if(0 == strncmp(suffixBegin, ".vsh", 4))
{
source->shaderType = GL_VERTEX_SHADER;
}
else
{
// Unknown suffix
source->shaderType = 0;
}
// more code follows
.
}
However, GL_FRAGMENT_SHADER is causing Xcode to stop any build with the error "Use of undeclared identifier 'GL_FRAGMENT_SHADER'" - similarly with the GL_VERTEX_SHADER. I presume there'll be more errors but currently this is what Xcode stops at.
You need: #import <OpenGL/gl.h>
Does Rust have a way to make a program pluggable. In C the plugins I create are .so files that I load with dlopen. Does Rust provide a native way of doing the same thing?
The Rust FAQ officially endorses libloading. Beyond that, there are three different options I know of:
Use the shared_library crate
Use the dylib crate.
Use std::dynamic_lib, which is deprecated since Rust 1.5. (These docs are no longer available in version 1.32; it's likely the feature has been dropped altogether by now.)
I haven't tried any of these, so I cannot really say which is best or what the pros/cons are for the different variants. I'd strongly advise against using std::dynamic_lib at least, given that it's deprecated and will likely be made private at some point in the future.
Exactly,
And below is the complete use case example:
use std::unstable::dynamic_lib::DynamicLibrary;
use std::os;
fn load_cuda_library()
{
let path = Path::new("/usr/lib/libcuda.so");
// Make sure the path contains a / or the linker will search for it.
let path = os::make_absolute(&path);
let lib = match DynamicLibrary::open(Some(&path)) {
Ok(lib) => lib,
Err(error) => fail!("Could not load the library: {}", error)
};
// load cuinit symbol
let cuInit: extern fn(u32) -> u32 = unsafe {
match lib.symbol("cuInit") {
Err(error) => fail!("Could not load function cuInit: {}", error),
Ok(cuInit) => cuInit
}
};
let argument = 0;
let expected_result = 0;
let result = cuInit(argument);
if result != expected_result {
fail!("cuInit({:?}) != {:?} but equaled {:?}",
argument, expected_result, result)
}
}
fn main()
{
load_cuda_library();
}
Yes. There's a module std::unstable::dynamic_lib that enables dynamic loading of libraries. It's undocumented, though, as it's a highly experimental API (everything in std::unstable is undocumented). As #dbaupp suggests, the source is the best documentation (current version is af9368452).
I have a few lines of assembler arm code in an .s file. Just a few routines i need to call. It works fine when building for the device, however when i switch to iPhone Simulator i get "no such instruction" errors. I tried to compile parts of the .s file conditionally with what i know:
#if !TARGET_IPHONE_SIMULATOR
But the assembler doesn't recognize these preprocessor directives (of course) and none of the conditional compilation techniques for assembler that i could remember or find worked, so i'm scratching my head now on how to avoid compilation of that assembler code when building for the Simulator. I also don't see a project option in Xcode that would allow me to compile the file or not depending on the target platform.
SOLVED:
All i was missing was the proper #import in the assembler file. I did not think of adding it because Xcode syntax highlighted any preprocessor directive in green (comment) which made me assume that these commands are not recognized when in fact they work just fine.
This works:
#import "TargetConditionals.h"
#if !TARGET_IPHONE_SIMULATOR
... asm code here ...
#endif
You do do it with a pre-processor macro. They are defined in TargetConditionals.h TARGET_IPHONE_SIMULATOR should be there! (You do need to #include it however.)
Here is code I use to detect ARM vs Thumb vs Simulator:
#include "TargetConditionals.h"
#if defined(__arm__)
# if defined(__thumb__)
# define COMPILE_ARM_THUMB_ASM 1
# else
# define COMPILE_ARM_ASM 1
# endif
#endif
#if TARGET_IPHONE_SIMULATOR
// Simulator defines
#else
// ARM or Thumb mode defines
#endif
// And here is how you might use it
uint32_t
test_compare_shifted_operand(uint32_t w1) {
uint32_t local;
#if defined(COMPILE_ARM_ASM)
const uint32_t shifted = (1 << 8);
__asm__ __volatile__ (
"mov %[w2], #1\n\t"
"cmp %[w2], %[w1], lsr #8\n\t"
"moveq %[w2], #10\n\t"
"movne %[w2], #11\n\t"
: \
[w1] "+l" (w1),
[w2] "+l" (local)
: \
[shifted] "l" (shifted)
);
#else // COMPILE_ARM_ASM
if ((w1 >> 8) == 1) {
local = 10;
} else {
local = 11;
}
#endif // COMPILE_ARM_ASM
return local;
}