Skip to content

Commit

Permalink
Parse command line.
Browse files Browse the repository at this point in the history
Currently, only flag --port is supported.

PiperOrigin-RevId: 681007712
Change-Id: Ifa0633ce9517544788954cecfa433256cf0914fe
  • Loading branch information
coeuvre authored and copybara-github committed Oct 1, 2024
1 parent 81b9287 commit a62a98f
Show file tree
Hide file tree
Showing 4 changed files with 218 additions and 12 deletions.
2 changes: 2 additions & 0 deletions src/tools/remote/src/main/cpp/testonly_output_service/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ cc_binary(
"memory.cc",
"memory.h",
"memory_unix.cc",
"string.cc",
"string.h",
],
deps = [
"//src/main/protobuf:bazel_output_service_cc_grpc",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,13 @@

#include "src/tools/remote/src/main/cpp/testonly_output_service/bazel_output_service_impl.h"

#include <iostream>
#include <stdint.h>
#include <stdio.h>

#include <memory>
#include <string>

#include "src/tools/remote/src/main/cpp/testonly_output_service/memory.h"
#include "src/tools/remote/src/main/cpp/testonly_output_service/string.h"
#include "grpcpp/security/server_credentials.h"
#include "grpcpp/server_builder.h"
#include "grpcpp/server_context.h"
Expand Down Expand Up @@ -65,18 +68,59 @@ grpc::Status BazelOutputServiceImpl::BatchStat(
return grpc::Status(grpc::StatusCode::UNIMPLEMENTED, "");
}

int RunServer(int argc, char** argv) {
BazelOutputServiceImpl service;
constexpr uint16_t kDefaultPort = 8080;

std::string server_address = "0.0.0.0:8080";
struct ParsedCommandLine {
Str8 error;
uint16_t port;
};

grpc::ServerBuilder builder;
builder.AddListeningPort(server_address, grpc::InsecureServerCredentials());
builder.RegisterService(&service);
std::unique_ptr<grpc::Server> server(builder.BuildAndStart());
std::cerr << "Server listening on " << server_address << std::endl;
static ParsedCommandLine* ParseCommandLine(Arena* arena, int argc,
char** argv) {
TemporaryMemory scratch = BeginScratch(arena);
ParsedCommandLine* result = PushArray(arena, ParsedCommandLine, 1);
result->port = kDefaultPort;
Str8 port_prefix = Str8FromCStr("--port=");
for (int i = 1; i < argc; ++i) {
Str8 arg = Str8FromCStr(argv[i]);
if (StartsWithStr8(arg, port_prefix)) {
Str8 port_str = PushSubStr8(scratch.arena, arg, port_prefix.len);
ParsedUInt32 port = ParseUInt32(port_str);
if (port.value) {
result->port = port.value;
} else {
result->error = PushStr8F(arena, "Not a valid port: %s", port_str.ptr);
break;
}
} else {
result->error = PushStr8F(arena, "Unknown command line: %s", arg.ptr);
break;
}
}
EndScratch(scratch);
return result;
}

int RunServer(int argc, char** argv) {
int exit_code = 0;
TemporaryMemory scratch = BeginScratch(0);
ParsedCommandLine* command_line = ParseCommandLine(scratch.arena, argc, argv);
if (IsEmptyStr8(command_line->error)) {
BazelOutputServiceImpl service;

server->Wait();
Str8 address = PushStr8F(scratch.arena, "0.0.0.0:%d", command_line->port);
grpc::ServerBuilder builder;
builder.AddListeningPort((char*)address.ptr,
grpc::InsecureServerCredentials());
builder.RegisterService(&service);
std::unique_ptr<grpc::Server> server = builder.BuildAndStart();
fprintf(stderr, "Server listening on port %d...\n", command_line->port);

return 0;
server->Wait();
} else {
fprintf(stderr, "%s\n", command_line->error.ptr);
exit_code = 1;
}
EndScratch(scratch);
return exit_code;
}
88 changes: 88 additions & 0 deletions src/tools/remote/src/main/cpp/testonly_output_service/string.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
// Copyright 2024 The Bazel Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include "src/tools/remote/src/main/cpp/testonly_output_service/string.h"

#include <assert.h>
#include <stdarg.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>

#include "src/tools/remote/src/main/cpp/testonly_output_service/memory.h"

ParsedUInt32 ParseUInt32(Str8 str) {
ParsedUInt32 result = {};

result.valid = true;
for (size_t i = 0; i < str.len; ++i) {
uint8_t ch = str.ptr[i];
if (ch < '0' || ch > '9') {
result.valid = false;
break;
}
result.value = result.value * 10 + ch - '0';
}

return result;
}

Str8 PushStr8(Arena *arena, Str8 str) {
uint8_t *ptr = PushArray(arena, uint8_t, str.len + 1);
memcpy(ptr, str.ptr, str.len + 1);
Str8 result = {ptr, str.len};
return result;
}

Str8 PushStr8F(Arena *arena, const char *format, ...) {
constexpr size_t kInitBufferSize = 256;
size_t buf_len = kInitBufferSize;
char *buf_ptr = PushArray(arena, char, buf_len);

va_list args;
va_start(args, format);
size_t str_len = vsnprintf(buf_ptr, buf_len, format, args);
va_end(args);

if (str_len + 1 <= buf_len) {
// Free the unused part of the buffer.
PopArena(arena, buf_len - str_len - 1);
} else {
// The buffer was too small. We need to resize it and try again.
PopArena(arena, buf_len);
buf_len = str_len + 1;
buf_ptr = PushArray(arena, char, buf_len);
va_start(args, format);
vsnprintf(buf_ptr, buf_len, format, args);
va_end(args);
}

Str8 result = {(uint8_t *)buf_ptr, str_len};
return result;
}

Str8 PushSubStr8(Arena *arena, Str8 str, size_t begin) {
Str8 result = PushSubStr8(arena, str, begin, str.len);
return result;
}

Str8 PushSubStr8(Arena *arena, Str8 str, size_t begin, size_t end) {
assert(begin <= end && end <= str.len);
size_t len = end - begin;
uint8_t *ptr = PushArray(arena, uint8_t, len + 1);
memcpy(ptr, str.ptr + begin, len);
Str8 result = {ptr, len};
return result;
}
72 changes: 72 additions & 0 deletions src/tools/remote/src/main/cpp/testonly_output_service/string.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
// Copyright 2024 The Bazel Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef BAZEL_SRC_TOOLS_REMOTE_SRC_MAIN_CPP_TESTONLY_OUTPUT_SERVICE_STRING_H_
#define BAZEL_SRC_TOOLS_REMOTE_SRC_MAIN_CPP_TESTONLY_OUTPUT_SERVICE_STRING_H_

#include <stdint.h>
#include <string.h>

#include "src/tools/remote/src/main/cpp/testonly_output_service/memory.h"

// Null terminated utf-8 string.
struct Str8 {
uint8_t *ptr;
// Length in bytes, excluding the null terminator. However, the string is
// always null terminated which means the memory pointed to by ptr must be
// at least len + 1 bytes long.
size_t len;
};

// Returns true if the string is empty.
static inline bool IsEmptyStr8(Str8 str) {
bool result = str.len == 0;
return result;
}

// Constructs a Str8 from a C string.
static inline Str8 Str8FromCStr(const char *str) {
Str8 result = {(uint8_t *)str, strlen(str)};
return result;
}

// Returns true if the string starts with the given prefix.
static inline bool StartsWithStr8(Str8 str, Str8 prefix) {
bool result =
str.len >= prefix.len && memcmp(str.ptr, prefix.ptr, prefix.len) == 0;
return result;
}

// The result of parsing an unsigned 32-bit integer from a string. The `value`
// is only valid if `valid` is true.
struct ParsedUInt32 {
bool valid;
uint32_t value;
};

// Parses an unsigned 32-bit integer from the given string.
ParsedUInt32 ParseUInt32(Str8 str);

// Pushes a copy of the given string to the arena
Str8 PushStr8(Arena *arena, Str8 str);
// Pushes a formatted string to the arena.
Str8 PushStr8F(Arena *arena, const char *format, ...);
// Pushes a substring of the given string to the arena, from the index `begin`
// to the end of the string.
Str8 PushSubStr8(Arena *arena, Str8 str, size_t begin);
// Pushes a substring of the given string to the arena, from the index `begin`
// to the index `end` (exclusive).
Str8 PushSubStr8(Arena *arena, Str8 str, size_t begin, size_t end);

#endif // BAZEL_SRC_TOOLS_REMOTE_SRC_MAIN_CPP_TESTONLY_OUTPUT_SERVICE_STRING_H_

0 comments on commit a62a98f

Please sign in to comment.