* -------------------------------------------------------------------------
* This file is part of the MindStudio project.
* Copyright (c) 2025 Huawei Technologies Co.,Ltd.
*
* MindStudio is licensed under Mulan PSL v2.
* You can use this software according to the terms and conditions of the Mulan PSL v2.
* You may obtain a copy of Mulan PSL v2 at:
*
* http://license.coscl.org.cn/MulanPSL2
*
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
* See the Mulan PSL v2 for more details.
* -------------------------------------------------------------------------
*/
#[cfg(windows)]
use std::os::windows::process::CommandExt;
use std::{
env,
env::current_exe,
net::{Ipv4Addr, SocketAddrV4, TcpListener},
path::PathBuf,
process::Command,
};
use crate::webview;
const SERVER_RELATIVE_LIST: [&str; 4] =
["resources", "profiler", "server", "profiler_server"];
#[cfg(windows)]
const NO_WINDOW_FLAG: u32 = 0x08000000;
fn server_path(root_path: &PathBuf) -> Option<PathBuf> {
let mut server_path = root_path.to_path_buf();
#[cfg(target_os = "macos")]
{
env::set_var(
"DYLD_LIBRARY_PATH",
server_path
.join("resources/profiler/server/:$DYLD_LIBRARY_PATH")
.as_path(),
);
}
#[cfg(target_os = "linux")]
{
let ld_library_path =
env::var_os("LD_LIBRARY_PATH").unwrap_or_default();
let new_ld_library_path = format!(
"{}:{}",
server_path.join("resources/profiler/server/").display(),
ld_library_path.to_string_lossy()
);
env::set_var("LD_LIBRARY_PATH", &new_ld_library_path);
}
for tmp in SERVER_RELATIVE_LIST {
server_path.push(tmp);
}
#[cfg(windows)]
server_path.set_extension("exe");
if !server_path.exists() {
return None;
}
Some(server_path)
}
fn run_server(root_path: &PathBuf, cache_path: &PathBuf, port: u16) {
let binding = server_path(root_path).unwrap();
let Some(path) = binding.to_str() else { unreachable!() };
let mut server_command = Command::new(path);
#[cfg(windows)]
server_command.creation_flags(NO_WINDOW_FLAG);
match server_command
.arg(format!("--wsPort={port}"))
.arg(format!("--logPath={}", cache_path.display()))
.arg("--notStrict")
.spawn()
{
Ok(child) => unsafe {
PID = child.id();
},
_ => eprintln!("Failed to start server"),
}
}
fn home_dir() -> Option<PathBuf> {
#[cfg(target_os = "windows")]
{
env::var("USERPROFILE").ok().map(PathBuf::from)
}
#[cfg(not(target_os = "windows"))]
{
env::var("HOME").ok().map(PathBuf::from)
}
}
#[cfg(windows)]
#[link(name = "shell32")]
extern "system" {
pub fn IsUserAnAdmin() -> bool;
}
#[cfg(windows)]
fn is_admin() -> bool {
unsafe {
IsUserAnAdmin()
}
}
#[cfg(windows)]
fn eq_prefix(lhs: &PathBuf, rhs: &PathBuf) -> bool {
match (lhs.components().next(), rhs.components().next()) {
(Some(l), Some(r)) => l == r,
_ => false,
}
}
fn find_first_available_port(start: u16, end: u16) -> Option<u16> {
for port in start..=end {
let addr = SocketAddrV4::new(Ipv4Addr::new(127, 0, 0, 1), port);
if TcpListener::bind(addr).is_ok() {
return Some(port);
}
}
None
}
pub static mut PID: u32 = u32::MAX;
pub fn main() {
let mut cache_path = home_dir()
.expect("Home dir not exists, check your env variable")
.join(".mindstudio_insight");
let root_path = current_exe()
.expect("Failed to get current exe path")
.parent()
.expect("Failed to get parent path of current exe")
.to_path_buf();
#[cfg(windows)]
{
let mut webview_path = cache_path.clone();
if !eq_prefix(&cache_path, &root_path) {
cache_path = root_path.join(".mindstudio_insight");
webview_path = cache_path.clone();
}
if is_admin() {
webview_path.push("admin");
}
env::set_var("WEBVIEW2_USER_DATA_FOLDER", webview_path.as_path());
}
if !cache_path.exists() {
#[cfg(windows)]
{
use std::fs::create_dir_all;
create_dir_all(cache_path.as_path())
.expect("no permission to create cache_path");
}
#[cfg(unix)]
{
use std::{fs::DirBuilder, os::unix::fs::DirBuilderExt};
DirBuilder::new()
.recursive(true)
.mode(0o750)
.create(cache_path.as_path())
.expect("no permission to create cache_path");
}
}
if webview::webview_version().is_err() {
#[cfg(windows)]
webview::webview2err::show_webview_err_message();
return;
}
let Some(port) = find_first_available_port(9000, 9100) else {
eprintln!("No available port between 9000 and 9100");
return;
};
if let Ok((eventloop, webview)) = webview::run_script(&root_path, &cache_path, port) {
run_server(&root_path, &cache_path, port);
webview::run_event_loop(eventloop, webview)
}
}